From ef2e26c91b80556af033d3335e55f5dfa6fff31d Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 13 Aug 2003 01:53:07 +0000 Subject: first public release of samba4 code (This used to be commit b0510b5428b3461aeb9bbe3cc95f62fc73e2b97f) --- source4/.cvsignore | 34 + source4/.dmallocrc | 2 + source4/Doxyfile | 176 + source4/Makefile.in | 1338 ++++ source4/aclocal.m4 | 643 ++ source4/auth/auth.c | 456 ++ source4/auth/auth_builtin.c | 210 + source4/auth/auth_compat.c | 122 + source4/auth/auth_domain.c | 554 ++ source4/auth/auth_ntlmssp.c | 138 + source4/auth/auth_sam.c | 564 ++ source4/auth/auth_server.c | 402 + source4/auth/auth_unix.c | 132 + source4/auth/auth_util.c | 1222 +++ source4/auth/auth_winbind.c | 136 + source4/auth/pampass.c | 875 +++ source4/auth/pass_check.c | 784 ++ source4/autogen.sh | 36 + source4/build/tests/README | 10 + source4/build/tests/crypttest.c | 852 +++ source4/build/tests/fcntl_lock.c | 121 + source4/build/tests/fcntl_lock64.c | 96 + source4/build/tests/fcntl_lock_thread.c | 122 + source4/build/tests/ftruncate.c | 27 + source4/build/tests/getgroups.c | 66 + source4/build/tests/shared_mmap.c | 68 + source4/build/tests/shlib.c | 6 + source4/build/tests/summary.c | 30 + source4/build/tests/trivial.c | 4 + source4/build/tests/unixsock.c | 93 + source4/change-log | 1878 +++++ source4/client/.cvsignore | 1 + source4/client/client.c | 3025 ++++++++ source4/client/clitar.c | 1856 +++++ source4/client/mount.cifs.c | 557 ++ source4/client/smbmnt.c | 306 + source4/client/smbmount.c | 930 +++ source4/client/smbspool.c | 362 + source4/client/smbumount.c | 186 + source4/client/tree.c | 811 ++ source4/codepages/.cvsignore | 0 source4/codepages/valid.dat | Bin 0 -> 65536 bytes source4/config.sub | 1473 ++++ source4/configure.developer | 2 + source4/configure.in | 3455 +++++++++ source4/configure.nodebug.developer | 3 + source4/configure.tridge.opt | 3 + source4/dynconfig.c | 72 + source4/groupdb/mapping.c | 1340 ++++ source4/ignore.txt | 4 + source4/include/.cvsignore | 8 + source4/include/MacExtensions.h | 246 + source4/include/ads.h | 215 + source4/include/adt_tree.h | 38 + source4/include/asn_1.h | 69 + source4/include/auth.h | 161 + source4/include/byteorder.h | 175 + source4/include/charset.h | 40 + source4/include/cli_context.h | 308 + source4/include/client.h | 118 + source4/include/clitar.h | 41 + source4/include/context.h | 346 + source4/include/debug.h | 49 + source4/include/dlinklist.h | 78 + source4/include/doserr.h | 233 + source4/include/dynconfig.h | 39 + source4/include/enums.h | 64 + source4/include/events.h | 75 + source4/include/genparser.h | 78 + source4/include/genparser_samba.h | 58 + source4/include/gums.h | 230 + source4/include/hmacmd5.h | 32 + source4/include/includes.h | 1237 +++ source4/include/interfaces.h | 12 + source4/include/intl.h | 24 + source4/include/ioctl.h | 30 + source4/include/libsmb_internal.h | 67 + source4/include/libsmbclient.h | 1073 +++ source4/include/local.h | 226 + source4/include/mangle.h | 14 + source4/include/mapping.h | 61 + source4/include/md5.h | 24 + source4/include/messages.h | 75 + source4/include/msdfs.h | 77 + source4/include/mutex.h | 79 + source4/include/nameserv.h | 644 ++ source4/include/nt_printing.h | 482 ++ source4/include/nt_status.h | 63 + source4/include/ntdomain.h | 379 + source4/include/nterr.h | 570 ++ source4/include/ntlmssp.h | 133 + source4/include/ntvfs.h | 86 + source4/include/passdb.h | 155 + source4/include/popt_common.h | 48 + source4/include/printing.h | 102 + source4/include/process_model.h | 49 + source4/include/pstring.h | 36 + source4/include/rap.h | 507 ++ source4/include/rpc_brs.h | 80 + source4/include/rpc_client.h | 28 + source4/include/rpc_creds.h | 96 + source4/include/rpc_dce.h | 320 + source4/include/rpc_dfs.h | 197 + source4/include/rpc_ds.h | 91 + source4/include/rpc_lsa.h | 760 ++ source4/include/rpc_misc.h | 429 ++ source4/include/rpc_netlogon.h | 910 +++ source4/include/rpc_parse.h | 30 + source4/include/rpc_reg.h | 644 ++ source4/include/rpc_samr.h | 1867 +++++ source4/include/rpc_secdes.h | 462 ++ source4/include/rpc_spoolss.h | 2228 ++++++ source4/include/rpc_srvsvc.h | 948 +++ source4/include/rpc_wkssvc.h | 72 + source4/include/safe_string.h | 99 + source4/include/sam.h | 238 + source4/include/secrets.h | 79 + source4/include/session.h | 40 + source4/include/smb.h | 1363 ++++ source4/include/smb_acls.h | 275 + source4/include/smb_interfaces.h | 1898 +++++ source4/include/smb_macros.h | 290 + source4/include/stamp-h.in | 1 + source4/include/talloc.h | 51 + source4/include/tdbsam2.h | 94 + source4/include/trans2.h | 428 ++ source4/include/util_getent.h | 61 + source4/include/version.h | 1 + source4/include/vt_mode.h | 48 + source4/include/xfile.h | 49 + source4/install-sh | 238 + source4/intl/.cvsignore | 2 + source4/intl/lang_tdb.c | 235 + source4/intl/linux-msg.sed | 100 + source4/lib/.cvsignore | 3 + source4/lib/account_pol.c | 169 + source4/lib/adt_tree.c | 464 ++ source4/lib/bitmap.c | 163 + source4/lib/charcnv.c | 925 +++ source4/lib/cmdline/popt_common.c | 327 + source4/lib/cmdline/readline.c | 159 + source4/lib/crc32.c | 71 + source4/lib/crypto/crc32.c | 71 + source4/lib/crypto/hmacmd5.c | 134 + source4/lib/crypto/md4.c | 175 + source4/lib/crypto/md5.c | 247 + source4/lib/data_blob.c | 141 + source4/lib/debug.c | 157 + source4/lib/dmallocmsg.c | 72 + source4/lib/dprintf.c | 113 + source4/lib/events.c | 372 + source4/lib/fault.c | 107 + source4/lib/fsusage.c | 148 + source4/lib/gencache.c | 372 + source4/lib/genparser.c | 786 ++ source4/lib/genparser_samba.c | 200 + source4/lib/genrand.c | 267 + source4/lib/getsmbpass.c | 156 + source4/lib/hmacmd5.c | 134 + source4/lib/iconv.c | 526 ++ source4/lib/interface.c | 333 + source4/lib/interfaces.c | 407 + source4/lib/ldap_escape.c | 90 + source4/lib/md4.c | 175 + source4/lib/md5.c | 247 + source4/lib/messages.c | 566 ++ source4/lib/module.c | 128 + source4/lib/ms_fnmatch.c | 226 + source4/lib/mutex.c | 142 + source4/lib/pam_errors.c | 126 + source4/lib/pidfile.c | 109 + source4/lib/popt/CHANGES | 43 + source4/lib/popt/COPYING | 22 + source4/lib/popt/README | 18 + source4/lib/popt/dummy.in | 0 source4/lib/popt/findme.c | 46 + source4/lib/popt/findme.h | 10 + source4/lib/popt/popt.c | 782 ++ source4/lib/popt/popt.h | 130 + source4/lib/popt/poptconfig.c | 142 + source4/lib/popt/popthelp.c | 301 + source4/lib/popt/poptint.h | 71 + source4/lib/popt/poptparse.c | 102 + source4/lib/popt/system.h | 53 + source4/lib/popt_common.c | 327 + source4/lib/readline.c | 159 + source4/lib/replace.c | 467 ++ source4/lib/select.c | 159 + source4/lib/sendfile.c | 382 + source4/lib/server_mutex.c | 58 + source4/lib/signal.c | 139 + source4/lib/smbpasswd.c | 200 + source4/lib/smbrun.c | 171 + source4/lib/snprintf.c | 978 +++ source4/lib/substitute.c | 188 + source4/lib/sysacls.c | 3198 ++++++++ source4/lib/system.c | 1114 +++ source4/lib/system_smbd.c | 119 + source4/lib/talloc.c | 515 ++ source4/lib/tallocmsg.c | 58 + source4/lib/talloctort.c | 65 + source4/lib/tdb/README | 167 + source4/lib/tdb/spinlock.c | 430 ++ source4/lib/tdb/spinlock.h | 55 + source4/lib/tdb/tdb.c | 2020 +++++ source4/lib/tdb/tdb.h | 144 + source4/lib/tdb/tdb.magic | 10 + source4/lib/tdb/tdbutil.c | 687 ++ source4/lib/tdb/tdbutil.h | 37 + source4/lib/time.c | 754 ++ source4/lib/username.c | 537 ++ source4/lib/util.c | 1000 +++ source4/lib/util_file.c | 531 ++ source4/lib/util_getent.c | 306 + source4/lib/util_pw.c | 89 + source4/lib/util_seaccess.c | 486 ++ source4/lib/util_sid.c | 631 ++ source4/lib/util_smbd.c | 65 + source4/lib/util_sock.c | 631 ++ source4/lib/util_str.c | 1619 ++++ source4/lib/util_unistr.c | 838 +++ source4/lib/util_uuid.c | 104 + source4/lib/wins_srv.c | 361 + source4/lib/xfile.c | 378 + source4/libads/.cvsignore | 2 + source4/libads/ads_ldap.c | 155 + source4/libads/ads_status.c | 133 + source4/libads/ads_struct.c | 141 + source4/libads/ads_utils.c | 181 + source4/libads/disp_sec.c | 159 + source4/libads/kerberos.c | 140 + source4/libads/kerberos_verify.c | 173 + source4/libads/krb5_setpw.c | 701 ++ source4/libads/ldap.c | 2098 ++++++ source4/libads/ldap_printer.c | 341 + source4/libads/ldap_user.c | 119 + source4/libads/ldap_utils.c | 96 + source4/libads/sasl.c | 427 ++ source4/libads/util.c | 60 + source4/libcli/.cvsignore | 3 + source4/libcli/cliconnect.c | 207 + source4/libcli/clideltree.c | 117 + source4/libcli/clidfs.c | 558 ++ source4/libcli/clifile.c | 647 ++ source4/libcli/clilist.c | 313 + source4/libcli/climessage.c | 93 + source4/libcli/clireadwrite.c | 155 + source4/libcli/clisecdesc.c | 121 + source4/libcli/clitrans2.c | 221 + source4/libcli/namecache.c | 246 + source4/libcli/namequery.c | 1333 ++++ source4/libcli/namequery_dc.c | 104 + source4/libcli/nmblib.c | 1287 ++++ source4/libcli/ntlmssp.c | 625 ++ source4/libcli/ntlmssp_parse.c | 303 + source4/libcli/raw/README | 5 + source4/libcli/raw/clikrb5.c | 399 + source4/libcli/raw/clioplock.c | 57 + source4/libcli/raw/clirewrite.c | 22 + source4/libcli/raw/clisession.c | 444 ++ source4/libcli/raw/clisocket.c | 148 + source4/libcli/raw/clispnego.c | 533 ++ source4/libcli/raw/clitransport.c | 218 + source4/libcli/raw/clitree.c | 290 + source4/libcli/raw/raweas.c | 147 + source4/libcli/raw/rawfile.c | 687 ++ source4/libcli/raw/rawfileinfo.c | 527 ++ source4/libcli/raw/rawfsinfo.c | 282 + source4/libcli/raw/rawioctl.c | 118 + source4/libcli/raw/rawnegotiate.c | 157 + source4/libcli/raw/rawnotify.c | 116 + source4/libcli/raw/rawreadwrite.c | 321 + source4/libcli/raw/rawrequest.c | 1019 +++ source4/libcli/raw/rawsearch.c | 569 ++ source4/libcli/raw/rawsetfileinfo.c | 335 + source4/libcli/raw/rawtrans.c | 489 ++ source4/libcli/raw/smb_signing.c | 341 + source4/libcli/unexpected.c | 172 + source4/libcli/util/asn1.c | 428 ++ source4/libcli/util/clierror.c | 99 + source4/libcli/util/cliutil.c | 110 + source4/libcli/util/credentials.c | 215 + source4/libcli/util/doserr.c | 91 + source4/libcli/util/errormap.c | 1546 ++++ source4/libcli/util/nterr.c | 715 ++ source4/libcli/util/ntlmssp_sign.c | 226 + source4/libcli/util/pwd_cache.c | 72 + source4/libcli/util/smbdes.c | 415 ++ source4/libcli/util/smbencrypt.c | 418 ++ source4/libcli/util/smberr.c | 181 + source4/locking/brlock.c | 698 ++ source4/locking/locking.c | 849 +++ source4/locking/posix.c | 1313 ++++ source4/modules/developer.c | 132 + source4/modules/mysql.c | 1043 +++ source4/modules/vfs_audit.c | 278 + source4/modules/vfs_extd_audit.c | 319 + source4/modules/vfs_fake_perms.c | 471 ++ source4/modules/vfs_netatalk.c | 429 ++ source4/modules/vfs_recycle.c | 648 ++ source4/modules/xml.c | 575 ++ source4/msdfs/README | 32 + source4/msdfs/msdfs.c | 913 +++ source4/nmbd/.cvsignore | 0 source4/nmbd/asyncdns.c | 349 + source4/nmbd/nmbd.c | 778 ++ source4/nmbd/nmbd_become_dmb.c | 396 + source4/nmbd/nmbd_become_lmb.c | 605 ++ source4/nmbd/nmbd_browserdb.c | 182 + source4/nmbd/nmbd_browsesync.c | 699 ++ source4/nmbd/nmbd_elections.c | 403 + source4/nmbd/nmbd_incomingdgrams.c | 858 +++ source4/nmbd/nmbd_incomingrequests.c | 596 ++ source4/nmbd/nmbd_lmhosts.c | 104 + source4/nmbd/nmbd_logonnames.c | 172 + source4/nmbd/nmbd_mynames.c | 228 + source4/nmbd/nmbd_namelistdb.c | 624 ++ source4/nmbd/nmbd_namequery.c | 304 + source4/nmbd/nmbd_nameregister.c | 522 ++ source4/nmbd/nmbd_namerelease.c | 222 + source4/nmbd/nmbd_nodestatus.c | 94 + source4/nmbd/nmbd_packets.c | 2013 +++++ source4/nmbd/nmbd_processlogon.c | 480 ++ source4/nmbd/nmbd_responserecordsdb.c | 264 + source4/nmbd/nmbd_sendannounce.c | 607 ++ source4/nmbd/nmbd_serverlistdb.c | 448 ++ source4/nmbd/nmbd_subnetdb.c | 361 + source4/nmbd/nmbd_synclists.c | 300 + source4/nmbd/nmbd_winsproxy.c | 221 + source4/nmbd/nmbd_winsserver.c | 2032 +++++ source4/nmbd/nmbd_workgroupdb.c | 342 + source4/nsswitch/.cvsignore | 4 + source4/nsswitch/README | 13 + source4/nsswitch/hp_nss_common.h | 51 + source4/nsswitch/hp_nss_dbdefs.h | 105 + source4/nsswitch/nss.h | 104 + source4/nsswitch/pam_winbind.c | 754 ++ source4/nsswitch/pam_winbind.h | 93 + source4/nsswitch/wb_client.c | 307 + source4/nsswitch/wb_common.c | 433 ++ source4/nsswitch/wbinfo.c | 891 +++ source4/nsswitch/winbind_client.h | 16 + source4/nsswitch/winbind_nss.c | 1341 ++++ source4/nsswitch/winbind_nss_config.h | 165 + source4/nsswitch/winbind_nss_solaris.c | 301 + source4/nsswitch/winbindd.c | 951 +++ source4/nsswitch/winbindd.h | 230 + source4/nsswitch/winbindd_ads.c | 837 +++ source4/nsswitch/winbindd_cache.c | 1016 +++ source4/nsswitch/winbindd_cm.c | 954 +++ source4/nsswitch/winbindd_dual.c | 210 + source4/nsswitch/winbindd_group.c | 896 +++ source4/nsswitch/winbindd_idmap.c | 196 + source4/nsswitch/winbindd_idmap_tdb.c | 441 ++ source4/nsswitch/winbindd_misc.c | 235 + source4/nsswitch/winbindd_nss.h | 242 + source4/nsswitch/winbindd_pam.c | 368 + source4/nsswitch/winbindd_rpc.c | 776 ++ source4/nsswitch/winbindd_sid.c | 235 + source4/nsswitch/winbindd_user.c | 607 ++ source4/nsswitch/winbindd_util.c | 553 ++ source4/nsswitch/winbindd_wins.c | 210 + source4/nsswitch/wins.c | 322 + source4/ntvfs/README | 26 + source4/ntvfs/cifs/README | 5 + source4/ntvfs/cifs/vfs_cifs.c | 723 ++ source4/ntvfs/ipc/README | 5 + source4/ntvfs/ipc/vfs_ipc.c | 295 + source4/ntvfs/ntvfs_base.c | 145 + source4/ntvfs/ntvfs_dfs.c | 117 + source4/ntvfs/ntvfs_generic.c | 544 ++ source4/ntvfs/ntvfs_util.c | 26 + source4/ntvfs/posix/vfs_posix.c | 151 + source4/ntvfs/print/README | 3 + source4/ntvfs/print/vfs_print.c | 104 + source4/ntvfs/reference/ref.h | 36 + source4/ntvfs/reference/ref_util.c | 142 + source4/ntvfs/reference/vfs_ref.c | 843 +++ source4/ntvfs/simple/svfs.h | 28 + source4/ntvfs/simple/svfs_util.c | 162 + source4/ntvfs/simple/vfs_simple.c | 848 +++ source4/pam_smbpass/CHANGELOG | 31 + source4/pam_smbpass/INSTALL | 64 + source4/pam_smbpass/README | 66 + source4/pam_smbpass/TODO | 7 + source4/pam_smbpass/general.h | 130 + source4/pam_smbpass/pam_smb_acct.c | 113 + source4/pam_smbpass/pam_smb_auth.c | 249 + source4/pam_smbpass/pam_smb_passwd.c | 330 + source4/pam_smbpass/samples/README | 3 + source4/pam_smbpass/samples/kdc-pdc | 15 + source4/pam_smbpass/samples/password-mature | 14 + source4/pam_smbpass/samples/password-migration | 18 + source4/pam_smbpass/samples/password-sync | 15 + source4/pam_smbpass/support.c | 637 ++ source4/pam_smbpass/support.h | 50 + source4/param/.cvsignore | 3 + source4/param/loadparm.c | 4136 +++++++++++ source4/param/params.c | 599 ++ source4/passdb/.cvsignore | 2 + source4/passdb/machine_sid.c | 192 + source4/passdb/passdb.c | 1167 +++ source4/passdb/pdb_compat.c | 115 + source4/passdb/pdb_get_set.c | 1123 +++ source4/passdb/pdb_guest.c | 123 + source4/passdb/pdb_interface.c | 855 +++ source4/passdb/pdb_ldap.c | 2089 ++++++ source4/passdb/pdb_nisplus.c | 1565 ++++ source4/passdb/pdb_smbpasswd.c | 1581 ++++ source4/passdb/pdb_tdb.c | 1007 +++ source4/passdb/pdb_unix.c | 110 + source4/passdb/privileges.c | 349 + source4/passdb/secrets.c | 612 ++ source4/passdb/util_sam_sid.c | 303 + source4/po/de.msg | 1707 +++++ source4/po/en.msg | 1707 +++++ source4/po/fr.msg | 1709 +++++ source4/po/it.msg | 1707 +++++ source4/po/ja.msg | 1821 +++++ source4/po/pl.msg | 1773 +++++ source4/po/tr.msg | 1723 +++++ source4/popt/.cvsignore | 8 + source4/popt/CHANGES | 43 + source4/popt/COPYING | 22 + source4/popt/README | 18 + source4/popt/dummy.in | 0 source4/popt/findme.c | 46 + source4/popt/findme.h | 10 + source4/popt/popt.c | 782 ++ source4/popt/popt.h | 130 + source4/popt/poptconfig.c | 142 + source4/popt/popthelp.c | 301 + source4/popt/poptint.h | 71 + source4/popt/poptparse.c | 102 + source4/popt/system.h | 53 + source4/printing/.cvsignore | 0 source4/printing/load.c | 73 + source4/printing/lpq_parse.c | 1099 +++ source4/printing/notify.c | 526 ++ source4/printing/nt_printing.c | 4887 ++++++++++++ source4/printing/pcap.c | 413 ++ source4/printing/print_cups.c | 1293 ++++ source4/printing/print_generic.c | 245 + source4/printing/print_svid.c | 144 + source4/printing/printfsp.c | 120 + source4/printing/printing.c | 2128 ++++++ source4/printing/printing_db.c | 204 + source4/python/.cvsignore | 1 + source4/python/README | 28 + source4/python/examples/spoolss/changeid.py | 34 + source4/python/examples/spoolss/enumprinters.py | 36 + source4/python/examples/spoolss/psec.py | 88 + source4/python/examples/tdbpack/.cvsignore | 2 + source4/python/examples/tdbpack/oldtdbutil.py | 144 + source4/python/examples/tdbpack/tdbtimetrial.py | 12 + source4/python/examples/tdbpack/test_tdbpack.py | 253 + source4/python/gprinterdata | 39 + source4/python/gtdbtool | 39 + source4/python/gtkdictbrowser.py | 272 + source4/python/mkpatch | 6 + source4/python/py_common.c | 259 + source4/python/py_common.h | 67 + source4/python/py_conv.c | 223 + source4/python/py_conv.h | 44 + source4/python/py_lsa.c | 468 ++ source4/python/py_lsa.h | 41 + source4/python/py_ntsec.c | 281 + source4/python/py_samba.c | 56 + source4/python/py_samr.c | 497 ++ source4/python/py_samr.h | 81 + source4/python/py_samr_conv.c | 58 + source4/python/py_smb.c | 399 + source4/python/py_smb.h | 39 + source4/python/py_spoolss.c | 484 ++ source4/python/py_spoolss.h | 160 + source4/python/py_spoolss_common.c | 35 + source4/python/py_spoolss_drivers.c | 421 ++ source4/python/py_spoolss_drivers_conv.c | 179 + source4/python/py_spoolss_forms.c | 266 + source4/python/py_spoolss_forms_conv.c | 91 + source4/python/py_spoolss_jobs.c | 377 + source4/python/py_spoolss_jobs_conv.c | 102 + source4/python/py_spoolss_ports.c | 137 + source4/python/py_spoolss_ports_conv.c | 58 + source4/python/py_spoolss_printerdata.c | 450 ++ source4/python/py_spoolss_printers.c | 475 ++ source4/python/py_spoolss_printers_conv.c | 354 + source4/python/py_srvsvc.c | 215 + source4/python/py_srvsvc.h | 26 + source4/python/py_srvsvc_conv.c | 43 + source4/python/py_tdb.c | 614 ++ source4/python/py_tdb.h | 26 + source4/python/py_tdbpack.c | 725 ++ source4/python/py_winbind.c | 724 ++ source4/python/py_winbind.h | 30 + source4/python/py_winbind_conv.c | 41 + source4/python/py_winreg.c | 82 + source4/python/py_winreg.h | 29 + source4/python/samba/.cvsignore | 1 + source4/python/samba/__init__.py | 7 + source4/python/samba/printerdata.py | 59 + source4/python/setup.py | 198 + source4/registry/reg_cachehook.c | 112 + source4/registry/reg_db.c | 311 + source4/registry/reg_frontend.c | 260 + source4/registry/reg_objects.c | 374 + source4/registry/reg_printing.c | 829 +++ source4/rpc_client/cli_dfs.c | 247 + source4/rpc_client/cli_ds.c | 73 + source4/rpc_client/cli_lsarpc.c | 1441 ++++ source4/rpc_client/cli_netlogon.c | 759 ++ source4/rpc_client/cli_pipe.c | 1357 ++++ source4/rpc_client/cli_reg.c | 103 + source4/rpc_client/cli_samr.c | 1445 ++++ source4/rpc_client/cli_spoolss.c | 2466 ++++++ source4/rpc_client/cli_spoolss_notify.c | 272 + source4/rpc_client/cli_srvsvc.c | 442 ++ source4/rpc_client/cli_wkssvc.c | 93 + source4/rpc_parse/.cvsignore | 2 + source4/rpc_parse/parse_dfs.c | 546 ++ source4/rpc_parse/parse_ds.c | 122 + source4/rpc_parse/parse_lsa.c | 2525 +++++++ source4/rpc_parse/parse_misc.c | 1784 +++++ source4/rpc_parse/parse_net.c | 2971 ++++++++ source4/rpc_parse/parse_prs.c | 1329 ++++ source4/rpc_parse/parse_reg.c | 1872 +++++ source4/rpc_parse/parse_rpc.c | 1106 +++ source4/rpc_parse/parse_samr.c | 7448 +++++++++++++++++++ source4/rpc_parse/parse_sec.c | 1028 +++ source4/rpc_parse/parse_spoolss.c | 7751 +++++++++++++++++++ source4/rpc_parse/parse_srv.c | 3590 +++++++++ source4/rpc_parse/parse_wks.c | 178 + source4/rpc_server/.cvsignore | 0 source4/rpc_server/srv_dfs.c | 177 + source4/rpc_server/srv_dfs_nt.c | 371 + source4/rpc_server/srv_lsa.c | 810 ++ source4/rpc_server/srv_lsa_hnd.c | 265 + source4/rpc_server/srv_lsa_nt.c | 1399 ++++ source4/rpc_server/srv_netlog.c | 345 + source4/rpc_server/srv_netlog_nt.c | 743 ++ source4/rpc_server/srv_pipe.c | 1386 ++++ source4/rpc_server/srv_pipe_hnd.c | 1156 +++ source4/rpc_server/srv_reg.c | 400 + source4/rpc_server/srv_reg_nt.c | 664 ++ source4/rpc_server/srv_samr.c | 1510 ++++ source4/rpc_server/srv_samr_nt.c | 4432 +++++++++++ source4/rpc_server/srv_samr_util.c | 437 ++ source4/rpc_server/srv_spoolss.c | 1649 ++++ source4/rpc_server/srv_spoolss_nt.c | 9079 +++++++++++++++++++++++ source4/rpc_server/srv_srvsvc.c | 557 ++ source4/rpc_server/srv_srvsvc_nt.c | 2138 ++++++ source4/rpc_server/srv_util.c | 546 ++ source4/rpc_server/srv_wkssvc.c | 75 + source4/rpc_server/srv_wkssvc_nt.c | 79 + source4/rpcclient/cmd_dfs.c | 237 + source4/rpcclient/cmd_ds.c | 59 + source4/rpcclient/cmd_lsarpc.c | 760 ++ source4/rpcclient/cmd_netlogon.c | 342 + source4/rpcclient/cmd_reg.c | 1007 +++ source4/rpcclient/cmd_samr.c | 1517 ++++ source4/rpcclient/cmd_spoolss.c | 2297 ++++++ source4/rpcclient/cmd_srvsvc.c | 361 + source4/rpcclient/cmd_wkssvc.c | 84 + source4/rpcclient/display_sec.c | 144 + source4/rpcclient/rpcclient.c | 756 ++ source4/rpcclient/rpcclient.h | 34 + source4/sam/SAM-interface_handles.txt | 123 + source4/sam/account.c | 305 + source4/sam/get_set_account.c | 845 +++ source4/sam/get_set_domain.c | 263 + source4/sam/get_set_group.c | 106 + source4/sam/group.c | 193 + source4/sam/gumm_tdb.c | 562 ++ source4/sam/gums.c | 131 + source4/sam/gums_api.c | 1268 ++++ source4/sam/gums_helper.c | 607 ++ source4/sam/interface.c | 1338 ++++ source4/sam/sam_ads.c | 1378 ++++ source4/sam/sam_plugin.c | 79 + source4/sam/sam_skel.c | 251 + source4/script/.cvsignore | 1 + source4/script/addtosmbpass | 74 + source4/script/build_env.sh | 35 + source4/script/convert_smbpasswd | 17 + source4/script/creategroup | 27 + source4/script/extract_allparms.sh | 2 + source4/script/find_missing_doc.pl | 90 + source4/script/findsmb.in | 152 + source4/script/findstatic.pl | 70 + source4/script/genstruct.pl | 298 + source4/script/installbin.sh | 40 + source4/script/installdat.sh | 23 + source4/script/installdirs.sh | 17 + source4/script/installman.sh | 71 + source4/script/installmodules.sh | 36 + source4/script/installscripts.sh | 47 + source4/script/installswat.sh | 127 + source4/script/makeunicodecasemap.awk | 59 + source4/script/mkinstalldirs | 38 + source4/script/mknissmbpasswd.sh | 31 + source4/script/mknissmbpwdtbl.sh | 42 + source4/script/mkproto.awk | 165 + source4/script/mkproto.sh | 43 + source4/script/mksmbpasswd.sh | 6 + source4/script/revert.sh | 18 + source4/script/scancvslog.pl | 112 + source4/script/smbtar | 165 + source4/script/uninstallbin.sh | 42 + source4/script/uninstallman.sh | 37 + source4/script/uninstallmodules.sh | 37 + source4/script/uninstallscripts.sh | 36 + source4/script/updatesmbpasswd.sh | 14 + source4/smbadduser | 73 + source4/smbd/.cvsignore | 2 + source4/smbd/build_options.c | 535 ++ source4/smbd/conn.c | 158 + source4/smbd/connection.c | 223 + source4/smbd/negprot.c | 526 ++ source4/smbd/password.c | 492 ++ source4/smbd/process.c | 833 +++ source4/smbd/process_model.c | 85 + source4/smbd/process_single.c | 86 + source4/smbd/process_standard.c | 110 + source4/smbd/process_thread.c | 410 + source4/smbd/reply.c | 2383 ++++++ source4/smbd/request.c | 571 ++ source4/smbd/rewrite.c | 82 + source4/smbd/server.c | 340 + source4/smbd/service.c | 339 + source4/smbd/session.c | 42 + source4/smbd/sesssetup.c | 149 + source4/smbd/tcon.c | 3 + source4/smbd/trans2.c | 1343 ++++ source4/smbwrapper/.cvsignore | 8 + source4/smbwrapper/PORTING | 77 + source4/smbwrapper/README | 94 + source4/smbwrapper/realcalls.c | 48 + source4/smbwrapper/realcalls.h | 263 + source4/smbwrapper/shared.c | 203 + source4/smbwrapper/smbsh.c | 127 + source4/smbwrapper/smbsh.in | 54 + source4/smbwrapper/smbw.c | 1554 ++++ source4/smbwrapper/smbw.h | 71 + source4/smbwrapper/smbw_cache.c | 207 + source4/smbwrapper/smbw_dir.c | 688 ++ source4/smbwrapper/smbw_stat.c | 250 + source4/smbwrapper/wrapped.c | 705 ++ source4/tdb/.cvsignore | 11 + source4/tdb/Makefile | 29 + source4/tdb/README | 167 + source4/tdb/spinlock.c | 430 ++ source4/tdb/spinlock.h | 55 + source4/tdb/tdb.c | 2020 +++++ source4/tdb/tdb.h | 144 + source4/tdb/tdb.magic | 10 + source4/tdb/tdbbackup.c | 315 + source4/tdb/tdbdump.c | 89 + source4/tdb/tdbtest.c | 263 + source4/tdb/tdbtool.c | 482 ++ source4/tdb/tdbtorture.c | 226 + source4/tdb/tdbutil.c | 687 ++ source4/tdb/tdbutil.h | 37 + source4/tests/.cvsignore | 3 + source4/tests/README | 10 + source4/tests/crypttest.c | 852 +++ source4/tests/fcntl_lock.c | 121 + source4/tests/fcntl_lock64.c | 96 + source4/tests/fcntl_lock_thread.c | 122 + source4/tests/ftruncate.c | 27 + source4/tests/getgroups.c | 66 + source4/tests/shared_mmap.c | 68 + source4/tests/shlib.c | 6 + source4/tests/summary.c | 30 + source4/tests/trivial.c | 4 + source4/tests/unixsock.c | 93 + source4/torture/.cvsignore | 1 + source4/torture/aliases.c | 403 + source4/torture/cmd_sam.c | 514 ++ source4/torture/cmd_vfs.c | 1051 +++ source4/torture/denytest.c | 1578 ++++ source4/torture/dfstest.c | 459 ++ source4/torture/genbit.c | 83 + source4/torture/gendefs.h | 83 + source4/torture/genparm.c | 732 ++ source4/torture/gentest.c | 2113 ++++++ source4/torture/locktest.c | 566 ++ source4/torture/locktest2.c | 558 ++ source4/torture/mangle_test.c | 205 + source4/torture/masktest.c | 464 ++ source4/torture/msgtest.c | 89 + source4/torture/nbio.c | 285 + source4/torture/nsstest.c | 410 + source4/torture/qfileinfo.c | 640 ++ source4/torture/qfsinfo.c | 286 + source4/torture/raw/chkpath.c | 142 + source4/torture/raw/close.c | 170 + source4/torture/raw/context.c | 388 + source4/torture/raw/ioctl.c | 156 + source4/torture/raw/lock.c | 216 + source4/torture/raw/missing.txt | 157 + source4/torture/raw/mkdir.c | 141 + source4/torture/raw/mux.c | 298 + source4/torture/raw/notify.c | 140 + source4/torture/raw/open.c | 895 +++ source4/torture/raw/oplock.c | 288 + source4/torture/raw/qfileinfo.c | 701 ++ source4/torture/raw/qfsinfo.c | 295 + source4/torture/raw/read.c | 732 ++ source4/torture/raw/rename.c | 128 + source4/torture/raw/search.c | 610 ++ source4/torture/raw/seek.c | 152 + source4/torture/raw/setfileinfo.c | 498 ++ source4/torture/raw/unlink.c | 148 + source4/torture/raw/write.c | 702 ++ source4/torture/rpctorture.c | 526 ++ source4/torture/samtest.c | 451 ++ source4/torture/samtest.h | 38 + source4/torture/scanner.c | 514 ++ source4/torture/search.c | 325 + source4/torture/setfileinfo.c | 471 ++ source4/torture/t_strcmp.c | 20 + source4/torture/torture.c | 4183 +++++++++++ source4/torture/torture_util.c | 306 + source4/torture/utable.c | 196 + source4/torture/vfstest.c | 591 ++ source4/torture/vfstest.h | 45 + source4/ubiqx/.cvsignore | 3 + source4/utils/.cvsignore | 1 + source4/utils/debug2html.c | 253 + source4/utils/editreg.c | 2069 ++++++ source4/utils/net.c | 642 ++ source4/utils/net.h | 61 + source4/utils/net_ads.c | 1176 +++ source4/utils/net_ads_cldap.c | 354 + source4/utils/net_cache.c | 348 + source4/utils/net_help.c | 199 + source4/utils/net_lookup.c | 234 + source4/utils/net_rap.c | 1051 +++ source4/utils/net_rpc.c | 2262 ++++++ source4/utils/net_rpc_join.c | 354 + source4/utils/net_rpc_samsync.c | 725 ++ source4/utils/net_time.c | 180 + source4/utils/nmblookup.c | 338 + source4/utils/ntlm_auth.c | 551 ++ source4/utils/pdbedit.c | 696 ++ source4/utils/profiles.c | 729 ++ source4/utils/rewrite.c | 32 + source4/utils/rpccheck.c | 62 + source4/utils/smbcacls.c | 937 +++ source4/utils/smbcontrol.c | 714 ++ source4/utils/smbfilter.c | 245 + source4/utils/smbgroupedit.c | 410 + source4/utils/smbpasswd.c | 605 ++ source4/utils/smbtree.c | 369 + source4/utils/smbw_sample.c | 94 + source4/utils/status.c | 665 ++ source4/utils/tdb/Makefile | 29 + source4/utils/tdb/README | 167 + source4/utils/tdb/tdb.magic | 10 + source4/utils/tdb/tdbbackup.c | 315 + source4/utils/tdb/tdbdump.c | 89 + source4/utils/tdb/tdbtest.c | 263 + source4/utils/tdb/tdbtool.c | 482 ++ source4/utils/tdb/tdbtorture.c | 226 + source4/utils/testparm.c | 338 + source4/utils/testprns.c | 61 + source4/web/.cvsignore | 1 + source4/web/cgi.c | 604 ++ source4/web/diagnose.c | 83 + source4/web/neg_lang.c | 117 + source4/web/startstop.c | 137 + source4/web/statuspage.c | 406 + source4/web/swat.c | 1344 ++++ source4/wrepld/parser.c | 759 ++ source4/wrepld/partners.c | 200 + source4/wrepld/process.c | 983 +++ source4/wrepld/server.c | 737 ++ source4/wrepld/socket.c | 69 + source4/wrepld/wins_repl.h | 161 + 779 files changed, 352638 insertions(+) create mode 100644 source4/.cvsignore create mode 100644 source4/.dmallocrc create mode 100644 source4/Doxyfile create mode 100644 source4/Makefile.in create mode 100644 source4/aclocal.m4 create mode 100644 source4/auth/auth.c create mode 100644 source4/auth/auth_builtin.c create mode 100644 source4/auth/auth_compat.c create mode 100644 source4/auth/auth_domain.c create mode 100644 source4/auth/auth_ntlmssp.c create mode 100644 source4/auth/auth_sam.c create mode 100644 source4/auth/auth_server.c create mode 100644 source4/auth/auth_unix.c create mode 100644 source4/auth/auth_util.c create mode 100644 source4/auth/auth_winbind.c create mode 100644 source4/auth/pampass.c create mode 100644 source4/auth/pass_check.c create mode 100755 source4/autogen.sh create mode 100644 source4/build/tests/README create mode 100644 source4/build/tests/crypttest.c create mode 100644 source4/build/tests/fcntl_lock.c create mode 100644 source4/build/tests/fcntl_lock64.c create mode 100644 source4/build/tests/fcntl_lock_thread.c create mode 100644 source4/build/tests/ftruncate.c create mode 100644 source4/build/tests/getgroups.c create mode 100644 source4/build/tests/shared_mmap.c create mode 100644 source4/build/tests/shlib.c create mode 100644 source4/build/tests/summary.c create mode 100644 source4/build/tests/trivial.c create mode 100644 source4/build/tests/unixsock.c create mode 100644 source4/change-log create mode 100644 source4/client/.cvsignore create mode 100644 source4/client/client.c create mode 100644 source4/client/clitar.c create mode 100644 source4/client/mount.cifs.c create mode 100644 source4/client/smbmnt.c create mode 100644 source4/client/smbmount.c create mode 100644 source4/client/smbspool.c create mode 100644 source4/client/smbumount.c create mode 100644 source4/client/tree.c create mode 100644 source4/codepages/.cvsignore create mode 100644 source4/codepages/valid.dat create mode 100755 source4/config.sub create mode 100755 source4/configure.developer create mode 100644 source4/configure.in create mode 100755 source4/configure.nodebug.developer create mode 100755 source4/configure.tridge.opt create mode 100644 source4/dynconfig.c create mode 100644 source4/groupdb/mapping.c create mode 100644 source4/ignore.txt create mode 100644 source4/include/.cvsignore create mode 100644 source4/include/MacExtensions.h create mode 100644 source4/include/ads.h create mode 100644 source4/include/adt_tree.h create mode 100644 source4/include/asn_1.h create mode 100644 source4/include/auth.h create mode 100644 source4/include/byteorder.h create mode 100644 source4/include/charset.h create mode 100644 source4/include/cli_context.h create mode 100644 source4/include/client.h create mode 100644 source4/include/clitar.h create mode 100644 source4/include/context.h create mode 100644 source4/include/debug.h create mode 100644 source4/include/dlinklist.h create mode 100644 source4/include/doserr.h create mode 100644 source4/include/dynconfig.h create mode 100644 source4/include/enums.h create mode 100644 source4/include/events.h create mode 100644 source4/include/genparser.h create mode 100644 source4/include/genparser_samba.h create mode 100644 source4/include/gums.h create mode 100644 source4/include/hmacmd5.h create mode 100644 source4/include/includes.h create mode 100644 source4/include/interfaces.h create mode 100644 source4/include/intl.h create mode 100644 source4/include/ioctl.h create mode 100644 source4/include/libsmb_internal.h create mode 100644 source4/include/libsmbclient.h create mode 100644 source4/include/local.h create mode 100644 source4/include/mangle.h create mode 100644 source4/include/mapping.h create mode 100644 source4/include/md5.h create mode 100644 source4/include/messages.h create mode 100644 source4/include/msdfs.h create mode 100644 source4/include/mutex.h create mode 100644 source4/include/nameserv.h create mode 100644 source4/include/nt_printing.h create mode 100644 source4/include/nt_status.h create mode 100644 source4/include/ntdomain.h create mode 100644 source4/include/nterr.h create mode 100644 source4/include/ntlmssp.h create mode 100644 source4/include/ntvfs.h create mode 100644 source4/include/passdb.h create mode 100644 source4/include/popt_common.h create mode 100644 source4/include/printing.h create mode 100644 source4/include/process_model.h create mode 100644 source4/include/pstring.h create mode 100755 source4/include/rap.h create mode 100644 source4/include/rpc_brs.h create mode 100644 source4/include/rpc_client.h create mode 100644 source4/include/rpc_creds.h create mode 100644 source4/include/rpc_dce.h create mode 100644 source4/include/rpc_dfs.h create mode 100644 source4/include/rpc_ds.h create mode 100644 source4/include/rpc_lsa.h create mode 100644 source4/include/rpc_misc.h create mode 100644 source4/include/rpc_netlogon.h create mode 100644 source4/include/rpc_parse.h create mode 100644 source4/include/rpc_reg.h create mode 100644 source4/include/rpc_samr.h create mode 100644 source4/include/rpc_secdes.h create mode 100755 source4/include/rpc_spoolss.h create mode 100644 source4/include/rpc_srvsvc.h create mode 100644 source4/include/rpc_wkssvc.h create mode 100644 source4/include/safe_string.h create mode 100644 source4/include/sam.h create mode 100644 source4/include/secrets.h create mode 100644 source4/include/session.h create mode 100644 source4/include/smb.h create mode 100644 source4/include/smb_acls.h create mode 100644 source4/include/smb_interfaces.h create mode 100644 source4/include/smb_macros.h create mode 100644 source4/include/stamp-h.in create mode 100644 source4/include/talloc.h create mode 100644 source4/include/tdbsam2.h create mode 100644 source4/include/trans2.h create mode 100644 source4/include/util_getent.h create mode 100644 source4/include/version.h create mode 100644 source4/include/vt_mode.h create mode 100644 source4/include/xfile.h create mode 100644 source4/install-sh create mode 100644 source4/intl/.cvsignore create mode 100644 source4/intl/lang_tdb.c create mode 100644 source4/intl/linux-msg.sed create mode 100644 source4/lib/.cvsignore create mode 100644 source4/lib/account_pol.c create mode 100644 source4/lib/adt_tree.c create mode 100644 source4/lib/bitmap.c create mode 100644 source4/lib/charcnv.c create mode 100644 source4/lib/cmdline/popt_common.c create mode 100644 source4/lib/cmdline/readline.c create mode 100644 source4/lib/crc32.c create mode 100644 source4/lib/crypto/crc32.c create mode 100644 source4/lib/crypto/hmacmd5.c create mode 100644 source4/lib/crypto/md4.c create mode 100644 source4/lib/crypto/md5.c create mode 100644 source4/lib/data_blob.c create mode 100644 source4/lib/debug.c create mode 100644 source4/lib/dmallocmsg.c create mode 100644 source4/lib/dprintf.c create mode 100644 source4/lib/events.c create mode 100644 source4/lib/fault.c create mode 100644 source4/lib/fsusage.c create mode 100644 source4/lib/gencache.c create mode 100644 source4/lib/genparser.c create mode 100644 source4/lib/genparser_samba.c create mode 100644 source4/lib/genrand.c create mode 100644 source4/lib/getsmbpass.c create mode 100644 source4/lib/hmacmd5.c create mode 100644 source4/lib/iconv.c create mode 100644 source4/lib/interface.c create mode 100644 source4/lib/interfaces.c create mode 100644 source4/lib/ldap_escape.c create mode 100644 source4/lib/md4.c create mode 100644 source4/lib/md5.c create mode 100644 source4/lib/messages.c create mode 100644 source4/lib/module.c create mode 100644 source4/lib/ms_fnmatch.c create mode 100644 source4/lib/mutex.c create mode 100644 source4/lib/pam_errors.c create mode 100644 source4/lib/pidfile.c create mode 100644 source4/lib/popt/CHANGES create mode 100644 source4/lib/popt/COPYING create mode 100644 source4/lib/popt/README create mode 100644 source4/lib/popt/dummy.in create mode 100644 source4/lib/popt/findme.c create mode 100644 source4/lib/popt/findme.h create mode 100644 source4/lib/popt/popt.c create mode 100644 source4/lib/popt/popt.h create mode 100644 source4/lib/popt/poptconfig.c create mode 100644 source4/lib/popt/popthelp.c create mode 100644 source4/lib/popt/poptint.h create mode 100644 source4/lib/popt/poptparse.c create mode 100644 source4/lib/popt/system.h create mode 100644 source4/lib/popt_common.c create mode 100644 source4/lib/readline.c create mode 100644 source4/lib/replace.c create mode 100644 source4/lib/select.c create mode 100644 source4/lib/sendfile.c create mode 100644 source4/lib/server_mutex.c create mode 100644 source4/lib/signal.c create mode 100644 source4/lib/smbpasswd.c create mode 100644 source4/lib/smbrun.c create mode 100644 source4/lib/snprintf.c create mode 100644 source4/lib/substitute.c create mode 100644 source4/lib/sysacls.c create mode 100644 source4/lib/system.c create mode 100644 source4/lib/system_smbd.c create mode 100644 source4/lib/talloc.c create mode 100644 source4/lib/tallocmsg.c create mode 100644 source4/lib/talloctort.c create mode 100644 source4/lib/tdb/README create mode 100644 source4/lib/tdb/spinlock.c create mode 100644 source4/lib/tdb/spinlock.h create mode 100644 source4/lib/tdb/tdb.c create mode 100644 source4/lib/tdb/tdb.h create mode 100644 source4/lib/tdb/tdb.magic create mode 100644 source4/lib/tdb/tdbutil.c create mode 100644 source4/lib/tdb/tdbutil.h create mode 100644 source4/lib/time.c create mode 100644 source4/lib/username.c create mode 100644 source4/lib/util.c create mode 100644 source4/lib/util_file.c create mode 100644 source4/lib/util_getent.c create mode 100644 source4/lib/util_pw.c create mode 100644 source4/lib/util_seaccess.c create mode 100644 source4/lib/util_sid.c create mode 100644 source4/lib/util_smbd.c create mode 100644 source4/lib/util_sock.c create mode 100644 source4/lib/util_str.c create mode 100644 source4/lib/util_unistr.c create mode 100644 source4/lib/util_uuid.c create mode 100644 source4/lib/wins_srv.c create mode 100644 source4/lib/xfile.c create mode 100644 source4/libads/.cvsignore create mode 100644 source4/libads/ads_ldap.c create mode 100644 source4/libads/ads_status.c create mode 100644 source4/libads/ads_struct.c create mode 100644 source4/libads/ads_utils.c create mode 100644 source4/libads/disp_sec.c create mode 100644 source4/libads/kerberos.c create mode 100644 source4/libads/kerberos_verify.c create mode 100644 source4/libads/krb5_setpw.c create mode 100644 source4/libads/ldap.c create mode 100644 source4/libads/ldap_printer.c create mode 100644 source4/libads/ldap_user.c create mode 100644 source4/libads/ldap_utils.c create mode 100644 source4/libads/sasl.c create mode 100644 source4/libads/util.c create mode 100644 source4/libcli/.cvsignore create mode 100644 source4/libcli/cliconnect.c create mode 100644 source4/libcli/clideltree.c create mode 100644 source4/libcli/clidfs.c create mode 100644 source4/libcli/clifile.c create mode 100644 source4/libcli/clilist.c create mode 100644 source4/libcli/climessage.c create mode 100644 source4/libcli/clireadwrite.c create mode 100644 source4/libcli/clisecdesc.c create mode 100644 source4/libcli/clitrans2.c create mode 100644 source4/libcli/namecache.c create mode 100644 source4/libcli/namequery.c create mode 100644 source4/libcli/namequery_dc.c create mode 100644 source4/libcli/nmblib.c create mode 100644 source4/libcli/ntlmssp.c create mode 100644 source4/libcli/ntlmssp_parse.c create mode 100644 source4/libcli/raw/README create mode 100644 source4/libcli/raw/clikrb5.c create mode 100644 source4/libcli/raw/clioplock.c create mode 100644 source4/libcli/raw/clirewrite.c create mode 100644 source4/libcli/raw/clisession.c create mode 100644 source4/libcli/raw/clisocket.c create mode 100644 source4/libcli/raw/clispnego.c create mode 100644 source4/libcli/raw/clitransport.c create mode 100644 source4/libcli/raw/clitree.c create mode 100644 source4/libcli/raw/raweas.c create mode 100644 source4/libcli/raw/rawfile.c create mode 100644 source4/libcli/raw/rawfileinfo.c create mode 100644 source4/libcli/raw/rawfsinfo.c create mode 100644 source4/libcli/raw/rawioctl.c create mode 100644 source4/libcli/raw/rawnegotiate.c create mode 100644 source4/libcli/raw/rawnotify.c create mode 100644 source4/libcli/raw/rawreadwrite.c create mode 100644 source4/libcli/raw/rawrequest.c create mode 100644 source4/libcli/raw/rawsearch.c create mode 100644 source4/libcli/raw/rawsetfileinfo.c create mode 100644 source4/libcli/raw/rawtrans.c create mode 100644 source4/libcli/raw/smb_signing.c create mode 100644 source4/libcli/unexpected.c create mode 100644 source4/libcli/util/asn1.c create mode 100644 source4/libcli/util/clierror.c create mode 100644 source4/libcli/util/cliutil.c create mode 100644 source4/libcli/util/credentials.c create mode 100644 source4/libcli/util/doserr.c create mode 100644 source4/libcli/util/errormap.c create mode 100644 source4/libcli/util/nterr.c create mode 100644 source4/libcli/util/ntlmssp_sign.c create mode 100644 source4/libcli/util/pwd_cache.c create mode 100644 source4/libcli/util/smbdes.c create mode 100644 source4/libcli/util/smbencrypt.c create mode 100644 source4/libcli/util/smberr.c create mode 100644 source4/locking/brlock.c create mode 100644 source4/locking/locking.c create mode 100644 source4/locking/posix.c create mode 100644 source4/modules/developer.c create mode 100644 source4/modules/mysql.c create mode 100644 source4/modules/vfs_audit.c create mode 100644 source4/modules/vfs_extd_audit.c create mode 100644 source4/modules/vfs_fake_perms.c create mode 100644 source4/modules/vfs_netatalk.c create mode 100644 source4/modules/vfs_recycle.c create mode 100644 source4/modules/xml.c create mode 100644 source4/msdfs/README create mode 100644 source4/msdfs/msdfs.c create mode 100644 source4/nmbd/.cvsignore create mode 100644 source4/nmbd/asyncdns.c create mode 100644 source4/nmbd/nmbd.c create mode 100644 source4/nmbd/nmbd_become_dmb.c create mode 100644 source4/nmbd/nmbd_become_lmb.c create mode 100644 source4/nmbd/nmbd_browserdb.c create mode 100644 source4/nmbd/nmbd_browsesync.c create mode 100644 source4/nmbd/nmbd_elections.c create mode 100644 source4/nmbd/nmbd_incomingdgrams.c create mode 100644 source4/nmbd/nmbd_incomingrequests.c create mode 100644 source4/nmbd/nmbd_lmhosts.c create mode 100644 source4/nmbd/nmbd_logonnames.c create mode 100644 source4/nmbd/nmbd_mynames.c create mode 100644 source4/nmbd/nmbd_namelistdb.c create mode 100644 source4/nmbd/nmbd_namequery.c create mode 100644 source4/nmbd/nmbd_nameregister.c create mode 100644 source4/nmbd/nmbd_namerelease.c create mode 100644 source4/nmbd/nmbd_nodestatus.c create mode 100644 source4/nmbd/nmbd_packets.c create mode 100644 source4/nmbd/nmbd_processlogon.c create mode 100644 source4/nmbd/nmbd_responserecordsdb.c create mode 100644 source4/nmbd/nmbd_sendannounce.c create mode 100644 source4/nmbd/nmbd_serverlistdb.c create mode 100644 source4/nmbd/nmbd_subnetdb.c create mode 100644 source4/nmbd/nmbd_synclists.c create mode 100644 source4/nmbd/nmbd_winsproxy.c create mode 100644 source4/nmbd/nmbd_winsserver.c create mode 100644 source4/nmbd/nmbd_workgroupdb.c create mode 100644 source4/nsswitch/.cvsignore create mode 100644 source4/nsswitch/README create mode 100644 source4/nsswitch/hp_nss_common.h create mode 100644 source4/nsswitch/hp_nss_dbdefs.h create mode 100644 source4/nsswitch/nss.h create mode 100644 source4/nsswitch/pam_winbind.c create mode 100644 source4/nsswitch/pam_winbind.h create mode 100644 source4/nsswitch/wb_client.c create mode 100644 source4/nsswitch/wb_common.c create mode 100644 source4/nsswitch/wbinfo.c create mode 100644 source4/nsswitch/winbind_client.h create mode 100644 source4/nsswitch/winbind_nss.c create mode 100644 source4/nsswitch/winbind_nss_config.h create mode 100644 source4/nsswitch/winbind_nss_solaris.c create mode 100644 source4/nsswitch/winbindd.c create mode 100644 source4/nsswitch/winbindd.h create mode 100644 source4/nsswitch/winbindd_ads.c create mode 100644 source4/nsswitch/winbindd_cache.c create mode 100644 source4/nsswitch/winbindd_cm.c create mode 100644 source4/nsswitch/winbindd_dual.c create mode 100644 source4/nsswitch/winbindd_group.c create mode 100644 source4/nsswitch/winbindd_idmap.c create mode 100644 source4/nsswitch/winbindd_idmap_tdb.c create mode 100644 source4/nsswitch/winbindd_misc.c create mode 100644 source4/nsswitch/winbindd_nss.h create mode 100644 source4/nsswitch/winbindd_pam.c create mode 100644 source4/nsswitch/winbindd_rpc.c create mode 100644 source4/nsswitch/winbindd_sid.c create mode 100644 source4/nsswitch/winbindd_user.c create mode 100644 source4/nsswitch/winbindd_util.c create mode 100644 source4/nsswitch/winbindd_wins.c create mode 100644 source4/nsswitch/wins.c create mode 100644 source4/ntvfs/README create mode 100644 source4/ntvfs/cifs/README create mode 100644 source4/ntvfs/cifs/vfs_cifs.c create mode 100644 source4/ntvfs/ipc/README create mode 100644 source4/ntvfs/ipc/vfs_ipc.c create mode 100644 source4/ntvfs/ntvfs_base.c create mode 100644 source4/ntvfs/ntvfs_dfs.c create mode 100644 source4/ntvfs/ntvfs_generic.c create mode 100644 source4/ntvfs/ntvfs_util.c create mode 100644 source4/ntvfs/posix/vfs_posix.c create mode 100644 source4/ntvfs/print/README create mode 100644 source4/ntvfs/print/vfs_print.c create mode 100644 source4/ntvfs/reference/ref.h create mode 100644 source4/ntvfs/reference/ref_util.c create mode 100644 source4/ntvfs/reference/vfs_ref.c create mode 100644 source4/ntvfs/simple/svfs.h create mode 100644 source4/ntvfs/simple/svfs_util.c create mode 100644 source4/ntvfs/simple/vfs_simple.c create mode 100644 source4/pam_smbpass/CHANGELOG create mode 100644 source4/pam_smbpass/INSTALL create mode 100644 source4/pam_smbpass/README create mode 100644 source4/pam_smbpass/TODO create mode 100644 source4/pam_smbpass/general.h create mode 100644 source4/pam_smbpass/pam_smb_acct.c create mode 100644 source4/pam_smbpass/pam_smb_auth.c create mode 100644 source4/pam_smbpass/pam_smb_passwd.c create mode 100644 source4/pam_smbpass/samples/README create mode 100644 source4/pam_smbpass/samples/kdc-pdc create mode 100644 source4/pam_smbpass/samples/password-mature create mode 100644 source4/pam_smbpass/samples/password-migration create mode 100644 source4/pam_smbpass/samples/password-sync create mode 100644 source4/pam_smbpass/support.c create mode 100644 source4/pam_smbpass/support.h create mode 100644 source4/param/.cvsignore create mode 100644 source4/param/loadparm.c create mode 100644 source4/param/params.c create mode 100644 source4/passdb/.cvsignore create mode 100644 source4/passdb/machine_sid.c create mode 100644 source4/passdb/passdb.c create mode 100644 source4/passdb/pdb_compat.c create mode 100644 source4/passdb/pdb_get_set.c create mode 100644 source4/passdb/pdb_guest.c create mode 100644 source4/passdb/pdb_interface.c create mode 100644 source4/passdb/pdb_ldap.c create mode 100644 source4/passdb/pdb_nisplus.c create mode 100644 source4/passdb/pdb_smbpasswd.c create mode 100644 source4/passdb/pdb_tdb.c create mode 100644 source4/passdb/pdb_unix.c create mode 100644 source4/passdb/privileges.c create mode 100644 source4/passdb/secrets.c create mode 100644 source4/passdb/util_sam_sid.c create mode 100644 source4/po/de.msg create mode 100644 source4/po/en.msg create mode 100644 source4/po/fr.msg create mode 100644 source4/po/it.msg create mode 100644 source4/po/ja.msg create mode 100644 source4/po/pl.msg create mode 100644 source4/po/tr.msg create mode 100644 source4/popt/.cvsignore create mode 100644 source4/popt/CHANGES create mode 100644 source4/popt/COPYING create mode 100644 source4/popt/README create mode 100644 source4/popt/dummy.in create mode 100644 source4/popt/findme.c create mode 100644 source4/popt/findme.h create mode 100644 source4/popt/popt.c create mode 100644 source4/popt/popt.h create mode 100644 source4/popt/poptconfig.c create mode 100644 source4/popt/popthelp.c create mode 100644 source4/popt/poptint.h create mode 100644 source4/popt/poptparse.c create mode 100644 source4/popt/system.h create mode 100644 source4/printing/.cvsignore create mode 100644 source4/printing/load.c create mode 100644 source4/printing/lpq_parse.c create mode 100644 source4/printing/notify.c create mode 100644 source4/printing/nt_printing.c create mode 100644 source4/printing/pcap.c create mode 100644 source4/printing/print_cups.c create mode 100644 source4/printing/print_generic.c create mode 100644 source4/printing/print_svid.c create mode 100644 source4/printing/printfsp.c create mode 100644 source4/printing/printing.c create mode 100644 source4/printing/printing_db.c create mode 100644 source4/python/.cvsignore create mode 100644 source4/python/README create mode 100755 source4/python/examples/spoolss/changeid.py create mode 100755 source4/python/examples/spoolss/enumprinters.py create mode 100755 source4/python/examples/spoolss/psec.py create mode 100644 source4/python/examples/tdbpack/.cvsignore create mode 100644 source4/python/examples/tdbpack/oldtdbutil.py create mode 100755 source4/python/examples/tdbpack/tdbtimetrial.py create mode 100755 source4/python/examples/tdbpack/test_tdbpack.py create mode 100755 source4/python/gprinterdata create mode 100755 source4/python/gtdbtool create mode 100755 source4/python/gtkdictbrowser.py create mode 100755 source4/python/mkpatch create mode 100644 source4/python/py_common.c create mode 100644 source4/python/py_common.h create mode 100644 source4/python/py_conv.c create mode 100644 source4/python/py_conv.h create mode 100644 source4/python/py_lsa.c create mode 100644 source4/python/py_lsa.h create mode 100644 source4/python/py_ntsec.c create mode 100644 source4/python/py_samba.c create mode 100644 source4/python/py_samr.c create mode 100644 source4/python/py_samr.h create mode 100644 source4/python/py_samr_conv.c create mode 100644 source4/python/py_smb.c create mode 100644 source4/python/py_smb.h create mode 100644 source4/python/py_spoolss.c create mode 100644 source4/python/py_spoolss.h create mode 100644 source4/python/py_spoolss_common.c create mode 100644 source4/python/py_spoolss_drivers.c create mode 100644 source4/python/py_spoolss_drivers_conv.c create mode 100644 source4/python/py_spoolss_forms.c create mode 100644 source4/python/py_spoolss_forms_conv.c create mode 100644 source4/python/py_spoolss_jobs.c create mode 100644 source4/python/py_spoolss_jobs_conv.c create mode 100644 source4/python/py_spoolss_ports.c create mode 100644 source4/python/py_spoolss_ports_conv.c create mode 100644 source4/python/py_spoolss_printerdata.c create mode 100644 source4/python/py_spoolss_printers.c create mode 100644 source4/python/py_spoolss_printers_conv.c create mode 100644 source4/python/py_srvsvc.c create mode 100644 source4/python/py_srvsvc.h create mode 100644 source4/python/py_srvsvc_conv.c create mode 100644 source4/python/py_tdb.c create mode 100644 source4/python/py_tdb.h create mode 100644 source4/python/py_tdbpack.c create mode 100644 source4/python/py_winbind.c create mode 100644 source4/python/py_winbind.h create mode 100644 source4/python/py_winbind_conv.c create mode 100644 source4/python/py_winreg.c create mode 100644 source4/python/py_winreg.h create mode 100644 source4/python/samba/.cvsignore create mode 100644 source4/python/samba/__init__.py create mode 100644 source4/python/samba/printerdata.py create mode 100755 source4/python/setup.py create mode 100644 source4/registry/reg_cachehook.c create mode 100644 source4/registry/reg_db.c create mode 100644 source4/registry/reg_frontend.c create mode 100644 source4/registry/reg_objects.c create mode 100644 source4/registry/reg_printing.c create mode 100644 source4/rpc_client/cli_dfs.c create mode 100644 source4/rpc_client/cli_ds.c create mode 100644 source4/rpc_client/cli_lsarpc.c create mode 100644 source4/rpc_client/cli_netlogon.c create mode 100644 source4/rpc_client/cli_pipe.c create mode 100644 source4/rpc_client/cli_reg.c create mode 100644 source4/rpc_client/cli_samr.c create mode 100644 source4/rpc_client/cli_spoolss.c create mode 100644 source4/rpc_client/cli_spoolss_notify.c create mode 100644 source4/rpc_client/cli_srvsvc.c create mode 100644 source4/rpc_client/cli_wkssvc.c create mode 100644 source4/rpc_parse/.cvsignore create mode 100644 source4/rpc_parse/parse_dfs.c create mode 100644 source4/rpc_parse/parse_ds.c create mode 100644 source4/rpc_parse/parse_lsa.c create mode 100644 source4/rpc_parse/parse_misc.c create mode 100644 source4/rpc_parse/parse_net.c create mode 100644 source4/rpc_parse/parse_prs.c create mode 100644 source4/rpc_parse/parse_reg.c create mode 100644 source4/rpc_parse/parse_rpc.c create mode 100644 source4/rpc_parse/parse_samr.c create mode 100644 source4/rpc_parse/parse_sec.c create mode 100644 source4/rpc_parse/parse_spoolss.c create mode 100644 source4/rpc_parse/parse_srv.c create mode 100644 source4/rpc_parse/parse_wks.c create mode 100644 source4/rpc_server/.cvsignore create mode 100644 source4/rpc_server/srv_dfs.c create mode 100644 source4/rpc_server/srv_dfs_nt.c create mode 100644 source4/rpc_server/srv_lsa.c create mode 100644 source4/rpc_server/srv_lsa_hnd.c create mode 100644 source4/rpc_server/srv_lsa_nt.c create mode 100644 source4/rpc_server/srv_netlog.c create mode 100644 source4/rpc_server/srv_netlog_nt.c create mode 100644 source4/rpc_server/srv_pipe.c create mode 100644 source4/rpc_server/srv_pipe_hnd.c create mode 100644 source4/rpc_server/srv_reg.c create mode 100644 source4/rpc_server/srv_reg_nt.c create mode 100644 source4/rpc_server/srv_samr.c create mode 100644 source4/rpc_server/srv_samr_nt.c create mode 100644 source4/rpc_server/srv_samr_util.c create mode 100755 source4/rpc_server/srv_spoolss.c create mode 100644 source4/rpc_server/srv_spoolss_nt.c create mode 100644 source4/rpc_server/srv_srvsvc.c create mode 100644 source4/rpc_server/srv_srvsvc_nt.c create mode 100644 source4/rpc_server/srv_util.c create mode 100644 source4/rpc_server/srv_wkssvc.c create mode 100644 source4/rpc_server/srv_wkssvc_nt.c create mode 100644 source4/rpcclient/cmd_dfs.c create mode 100644 source4/rpcclient/cmd_ds.c create mode 100644 source4/rpcclient/cmd_lsarpc.c create mode 100644 source4/rpcclient/cmd_netlogon.c create mode 100644 source4/rpcclient/cmd_reg.c create mode 100644 source4/rpcclient/cmd_samr.c create mode 100644 source4/rpcclient/cmd_spoolss.c create mode 100644 source4/rpcclient/cmd_srvsvc.c create mode 100644 source4/rpcclient/cmd_wkssvc.c create mode 100644 source4/rpcclient/display_sec.c create mode 100644 source4/rpcclient/rpcclient.c create mode 100644 source4/rpcclient/rpcclient.h create mode 100644 source4/sam/SAM-interface_handles.txt create mode 100644 source4/sam/account.c create mode 100644 source4/sam/get_set_account.c create mode 100644 source4/sam/get_set_domain.c create mode 100644 source4/sam/get_set_group.c create mode 100644 source4/sam/group.c create mode 100644 source4/sam/gumm_tdb.c create mode 100644 source4/sam/gums.c create mode 100644 source4/sam/gums_api.c create mode 100644 source4/sam/gums_helper.c create mode 100644 source4/sam/interface.c create mode 100755 source4/sam/sam_ads.c create mode 100644 source4/sam/sam_plugin.c create mode 100644 source4/sam/sam_skel.c create mode 100644 source4/script/.cvsignore create mode 100644 source4/script/addtosmbpass create mode 100644 source4/script/build_env.sh create mode 100755 source4/script/convert_smbpasswd create mode 100755 source4/script/creategroup create mode 100755 source4/script/extract_allparms.sh create mode 100755 source4/script/find_missing_doc.pl create mode 100755 source4/script/findsmb.in create mode 100755 source4/script/findstatic.pl create mode 100755 source4/script/genstruct.pl create mode 100644 source4/script/installbin.sh create mode 100755 source4/script/installdat.sh create mode 100755 source4/script/installdirs.sh create mode 100644 source4/script/installman.sh create mode 100644 source4/script/installmodules.sh create mode 100644 source4/script/installscripts.sh create mode 100644 source4/script/installswat.sh create mode 100644 source4/script/makeunicodecasemap.awk create mode 100755 source4/script/mkinstalldirs create mode 100755 source4/script/mknissmbpasswd.sh create mode 100755 source4/script/mknissmbpwdtbl.sh create mode 100644 source4/script/mkproto.awk create mode 100644 source4/script/mkproto.sh create mode 100644 source4/script/mksmbpasswd.sh create mode 100644 source4/script/revert.sh create mode 100755 source4/script/scancvslog.pl create mode 100644 source4/script/smbtar create mode 100644 source4/script/uninstallbin.sh create mode 100644 source4/script/uninstallman.sh create mode 100644 source4/script/uninstallmodules.sh create mode 100644 source4/script/uninstallscripts.sh create mode 100644 source4/script/updatesmbpasswd.sh create mode 100755 source4/smbadduser create mode 100644 source4/smbd/.cvsignore create mode 100644 source4/smbd/build_options.c create mode 100644 source4/smbd/conn.c create mode 100644 source4/smbd/connection.c create mode 100644 source4/smbd/negprot.c create mode 100644 source4/smbd/password.c create mode 100644 source4/smbd/process.c create mode 100644 source4/smbd/process_model.c create mode 100644 source4/smbd/process_single.c create mode 100644 source4/smbd/process_standard.c create mode 100644 source4/smbd/process_thread.c create mode 100644 source4/smbd/reply.c create mode 100644 source4/smbd/request.c create mode 100644 source4/smbd/rewrite.c create mode 100644 source4/smbd/server.c create mode 100644 source4/smbd/service.c create mode 100644 source4/smbd/session.c create mode 100644 source4/smbd/sesssetup.c create mode 100644 source4/smbd/tcon.c create mode 100644 source4/smbd/trans2.c create mode 100644 source4/smbwrapper/.cvsignore create mode 100644 source4/smbwrapper/PORTING create mode 100644 source4/smbwrapper/README create mode 100644 source4/smbwrapper/realcalls.c create mode 100644 source4/smbwrapper/realcalls.h create mode 100644 source4/smbwrapper/shared.c create mode 100644 source4/smbwrapper/smbsh.c create mode 100644 source4/smbwrapper/smbsh.in create mode 100644 source4/smbwrapper/smbw.c create mode 100644 source4/smbwrapper/smbw.h create mode 100644 source4/smbwrapper/smbw_cache.c create mode 100644 source4/smbwrapper/smbw_dir.c create mode 100644 source4/smbwrapper/smbw_stat.c create mode 100644 source4/smbwrapper/wrapped.c create mode 100644 source4/tdb/.cvsignore create mode 100644 source4/tdb/Makefile create mode 100644 source4/tdb/README create mode 100644 source4/tdb/spinlock.c create mode 100644 source4/tdb/spinlock.h create mode 100644 source4/tdb/tdb.c create mode 100644 source4/tdb/tdb.h create mode 100644 source4/tdb/tdb.magic create mode 100644 source4/tdb/tdbbackup.c create mode 100644 source4/tdb/tdbdump.c create mode 100644 source4/tdb/tdbtest.c create mode 100644 source4/tdb/tdbtool.c create mode 100644 source4/tdb/tdbtorture.c create mode 100644 source4/tdb/tdbutil.c create mode 100644 source4/tdb/tdbutil.h create mode 100644 source4/tests/.cvsignore create mode 100644 source4/tests/README create mode 100644 source4/tests/crypttest.c create mode 100644 source4/tests/fcntl_lock.c create mode 100644 source4/tests/fcntl_lock64.c create mode 100644 source4/tests/fcntl_lock_thread.c create mode 100644 source4/tests/ftruncate.c create mode 100644 source4/tests/getgroups.c create mode 100644 source4/tests/shared_mmap.c create mode 100644 source4/tests/shlib.c create mode 100644 source4/tests/summary.c create mode 100644 source4/tests/trivial.c create mode 100644 source4/tests/unixsock.c create mode 100644 source4/torture/.cvsignore create mode 100644 source4/torture/aliases.c create mode 100644 source4/torture/cmd_sam.c create mode 100644 source4/torture/cmd_vfs.c create mode 100644 source4/torture/denytest.c create mode 100644 source4/torture/dfstest.c create mode 100644 source4/torture/genbit.c create mode 100644 source4/torture/gendefs.h create mode 100644 source4/torture/genparm.c create mode 100644 source4/torture/gentest.c create mode 100644 source4/torture/locktest.c create mode 100644 source4/torture/locktest2.c create mode 100644 source4/torture/mangle_test.c create mode 100644 source4/torture/masktest.c create mode 100644 source4/torture/msgtest.c create mode 100644 source4/torture/nbio.c create mode 100644 source4/torture/nsstest.c create mode 100644 source4/torture/qfileinfo.c create mode 100644 source4/torture/qfsinfo.c create mode 100644 source4/torture/raw/chkpath.c create mode 100644 source4/torture/raw/close.c create mode 100644 source4/torture/raw/context.c create mode 100644 source4/torture/raw/ioctl.c create mode 100644 source4/torture/raw/lock.c create mode 100644 source4/torture/raw/missing.txt create mode 100644 source4/torture/raw/mkdir.c create mode 100644 source4/torture/raw/mux.c create mode 100644 source4/torture/raw/notify.c create mode 100644 source4/torture/raw/open.c create mode 100644 source4/torture/raw/oplock.c create mode 100644 source4/torture/raw/qfileinfo.c create mode 100644 source4/torture/raw/qfsinfo.c create mode 100644 source4/torture/raw/read.c create mode 100644 source4/torture/raw/rename.c create mode 100644 source4/torture/raw/search.c create mode 100644 source4/torture/raw/seek.c create mode 100644 source4/torture/raw/setfileinfo.c create mode 100644 source4/torture/raw/unlink.c create mode 100644 source4/torture/raw/write.c create mode 100644 source4/torture/rpctorture.c create mode 100644 source4/torture/samtest.c create mode 100644 source4/torture/samtest.h create mode 100644 source4/torture/scanner.c create mode 100644 source4/torture/search.c create mode 100644 source4/torture/setfileinfo.c create mode 100644 source4/torture/t_strcmp.c create mode 100644 source4/torture/torture.c create mode 100644 source4/torture/torture_util.c create mode 100644 source4/torture/utable.c create mode 100644 source4/torture/vfstest.c create mode 100644 source4/torture/vfstest.h create mode 100644 source4/ubiqx/.cvsignore create mode 100644 source4/utils/.cvsignore create mode 100644 source4/utils/debug2html.c create mode 100644 source4/utils/editreg.c create mode 100644 source4/utils/net.c create mode 100644 source4/utils/net.h create mode 100644 source4/utils/net_ads.c create mode 100644 source4/utils/net_ads_cldap.c create mode 100644 source4/utils/net_cache.c create mode 100644 source4/utils/net_help.c create mode 100644 source4/utils/net_lookup.c create mode 100644 source4/utils/net_rap.c create mode 100644 source4/utils/net_rpc.c create mode 100644 source4/utils/net_rpc_join.c create mode 100644 source4/utils/net_rpc_samsync.c create mode 100644 source4/utils/net_time.c create mode 100644 source4/utils/nmblookup.c create mode 100644 source4/utils/ntlm_auth.c create mode 100644 source4/utils/pdbedit.c create mode 100644 source4/utils/profiles.c create mode 100644 source4/utils/rewrite.c create mode 100644 source4/utils/rpccheck.c create mode 100644 source4/utils/smbcacls.c create mode 100644 source4/utils/smbcontrol.c create mode 100644 source4/utils/smbfilter.c create mode 100644 source4/utils/smbgroupedit.c create mode 100644 source4/utils/smbpasswd.c create mode 100644 source4/utils/smbtree.c create mode 100644 source4/utils/smbw_sample.c create mode 100644 source4/utils/status.c create mode 100644 source4/utils/tdb/Makefile create mode 100644 source4/utils/tdb/README create mode 100644 source4/utils/tdb/tdb.magic create mode 100644 source4/utils/tdb/tdbbackup.c create mode 100644 source4/utils/tdb/tdbdump.c create mode 100644 source4/utils/tdb/tdbtest.c create mode 100644 source4/utils/tdb/tdbtool.c create mode 100644 source4/utils/tdb/tdbtorture.c create mode 100644 source4/utils/testparm.c create mode 100644 source4/utils/testprns.c create mode 100644 source4/web/.cvsignore create mode 100644 source4/web/cgi.c create mode 100644 source4/web/diagnose.c create mode 100644 source4/web/neg_lang.c create mode 100644 source4/web/startstop.c create mode 100644 source4/web/statuspage.c create mode 100644 source4/web/swat.c create mode 100644 source4/wrepld/parser.c create mode 100644 source4/wrepld/partners.c create mode 100644 source4/wrepld/process.c create mode 100644 source4/wrepld/server.c create mode 100644 source4/wrepld/socket.c create mode 100644 source4/wrepld/wins_repl.h diff --git a/source4/.cvsignore b/source4/.cvsignore new file mode 100644 index 0000000000..d1c78732c9 --- /dev/null +++ b/source4/.cvsignore @@ -0,0 +1,34 @@ +libsmb +*.po +*.po32 +.headers.stamp +.inslog2 +.ix* +.proto.check +.proto.stamp +autom4te.cache +ID +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 +config.jjm +config.stfs +*.dat diff --git a/source4/.dmallocrc b/source4/.dmallocrc new file mode 100644 index 0000000000..5e5c45e124 --- /dev/null +++ b/source4/.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/source4/Doxyfile b/source4/Doxyfile new file mode 100644 index 0000000000..c1040781d8 --- /dev/null +++ b/source4/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/source4/Makefile.in b/source4/Makefile.in new file mode 100644 index 0000000000..d6541a7b7a --- /dev/null +++ b/source4/Makefile.in @@ -0,0 +1,1338 @@ +######################################################################### +# Makefile.in for Samba - rewritten for autoconf support +# Copyright Andrew Tridgell 1992-1998 +# Copyright (C) 2001 by Martin Pool +# Copyright Andrew Barteltt 2002 +# Copyright (C) 2003 Anthony Liguori +# Copyright (C) 2003 James Myers +########################################################################### + +prefix=@prefix@ +exec_prefix=@exec_prefix@ +mandir=@mandir@ + +LIBS=@LIBS@ +CC=@CC@ +SHLD=@SHLD@ +CFLAGS=@CFLAGS@ +CPPFLAGS=@CPPFLAGS@ +EXEEXT=@EXEEXT@ +LDFLAGS=@LDFLAGS@ +LDSHFLAGS=@LDSHFLAGS@ @LDFLAGS@ @CFLAGS@ +AWK=@AWK@ +DYNEXP=@DYNEXP@ +PYTHON=@PYTHON@ + +TERMLDFLAGS=@TERMLDFLAGS@ +TERMLIBS=@TERMLIBS@ +PRINTLIBS=@PRINTLIBS@ +AUTHLIBS=@AUTHLIBS@ +ACLLIBS=@ACLLIBS@ + +LINK=$(CC) $(FLAGS) $(LDFLAGS) + +INSTALLCMD=@INSTALL@ +INSTALLCLIENTCMD_SH=@INSTALLCLIENTCMD_SH@ +INSTALLCLIENTCMD_A=@INSTALLCLIENTCMD_A@ + +VPATH=@srcdir@ +srcdir=@srcdir@ +builddir=@builddir@ +SHELL=/bin/sh + +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 +CONFIGDIR = @configdir@ +VARDIR = @localstatedir@ +MANDIR = @mandir@ + +# 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_MAJOR=0 +LIBSMBCLIENT_MINOR=1 + + +FLAGS1 = $(CFLAGS) @FLAGS1@ -Iinclude -I$(srcdir)/include -I$(srcdir)/ubiqx -I. $(CPPFLAGS) -I$(srcdir) +FLAGS2 = -I/usr/src/newport/csm/include/linuxusp -I/usr/src/newport/csm/include/common -I/usr/src/newport/stp/include +FLAGS3 = +FLAGS4 = +FLAGS5 = $(FLAGS1) $(FLAGS2) $(FLAGS3) $(FLAGS4) +FLAGS = $(ISA) $(FLAGS5) +FLAGS32 = $(ISA32) $(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@ \ + bin/wrepld@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_PROGS3 = bin/smbpasswd@EXEEXT@ bin/rpcclient@EXEEXT@ bin/smbcacls@EXEEXT@ \ + bin/profiles@EXEEXT@ bin/smbgroupedit@EXEEXT@ bin/ntlm_auth@EXEEXT@ \ + bin/editreg@EXEEXT@ + +TORTURE_PROGS = bin/smbtorture@EXEEXT@ bin/gentest@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@ + +SHLIBS = @SHLIB_PROGS@ @LIBSMBCLIENT@ + +SCRIPTS = $(srcdir)/script/smbtar $(srcdir)/script/addtosmbpass $(srcdir)/script/convert_smbpasswd \ + $(builddir)/script/findsmb + +# QUOTAOBJS=@QUOTAOBJS@ + +VFS_MODULES = bin/vfs_audit.@SHLIBEXT@ bin/vfs_extd_audit.@SHLIBEXT@ bin/vfs_recycle.@SHLIBEXT@ \ + bin/vfs_netatalk.@SHLIBEXT@ bin/vfs_fake_perms.@SHLIBEXT@ +PDB_MODULES = @MODULE_MYSQL@ @MODULE_XML@ +MODULES = bin/developer.@SHLIBEXT@ + +###################################################################### +# object file lists +###################################################################### + +TDBBASE_OBJ = tdb/tdb.o tdb/spinlock.o +TDB_OBJ = $(TDBBASE_OBJ) tdb/tdbutil.o + +LIB_OBJ = 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/signal.o lib/system.o lib/sendfile.o lib/time.o \ + lib/genrand.o lib/username.o \ + lib/util_getent.o lib/util_pw.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/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/talloc.o lib/substitute.o lib/fsusage.o \ + lib/ms_fnmatch.o lib/select.o lib/messages.o \ + lib/tallocmsg.o lib/dmallocmsg.o \ + lib/md5.o lib/hmacmd5.o lib/iconv.o lib/smbpasswd.o \ + nsswitch/wb_client.o nsswitch/wb_common.o \ + lib/pam_errors.o intl/lang_tdb.o lib/account_pol.o \ + lib/gencache.o $(TDB_OBJ) \ + lib/module.o lib/genparser.o \ + lib/ldap_escape.o lib/events.o lib/mutex.o + +LIB_SMBD_OBJ = lib/system_smbd.o lib/util_smbd.o $(LIB_OBJ) + +READLINE_OBJ = lib/readline.o + +POPT_LIB_OBJ = lib/popt_common.o + +PARAM_OBJ = param/loadparm.o param/params.o dynconfig.o + +KRBCLIENT_OBJ = libads/kerberos.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/ads_status.o \ +# libads/disp_sec.o libads/ads_utils.o libads/ldap_utils.o \ +# libads/ads_ldap.o + +#LIBADS_SERVER_OBJ = libads/util.o libads/kerberos_verify.o + +SECRETS_OBJ = passdb/secrets.o + +LIBNMB_OBJ = libcli/unexpected.o libcli/namecache.o libcli/nmblib.o \ + libcli/namequery.o + +LIBNTLMSSP_OBJ = libcli/ntlmssp.o libcli/ntlmssp_parse.o libcli/util/ntlmssp_sign.o + +LIBSAMBA_OBJ = libcli/util/nterr.o libcli/util/smbdes.o libcli/util/smbencrypt.o + +LIBCLIUTIL_OBJ = libcli/util/asn1.o \ + libcli/util/smberr.o libcli/util/credentials.o \ + libcli/util/doserr.o libcli/util/errormap.o \ + libcli/util/pwd_cache.o libcli/util/clierror.o libcli/util/cliutil.o + +LIBRAW_OBJ = libcli/raw/rawfile.o libcli/raw/smb_signing.o \ + libcli/raw/clisocket.o libcli/raw/clitransport.o \ + libcli/raw/clisession.o libcli/raw/clitree.o \ + libcli/raw/clikrb5.o libcli/raw/clispnego.o libcli/raw/rawrequest.o \ + libcli/raw/rawreadwrite.o \ + libcli/raw/rawsearch.o libcli/raw/rawsetfileinfo.o libcli/raw/raweas.o \ + libcli/raw/rawtrans.o libcli/raw/clioplock.o \ + libcli/raw/rawnegotiate.o libcli/raw/rawfsinfo.o \ + libcli/raw/rawfileinfo.o libcli/raw/rawnotify.o \ + libcli/raw/rawioctl.o \ + $(LIBSAMBA_OBJ) $(LIBCLIUTIL_OBJ) \ + $(RPC_PARSE_OBJ1) $(LIBNTLMSSP_OBJ) $(LIBNMB_OBJ) $(KRBCLIENT_OBJ) + +LIBSMB_OBJ = libcli/clireadwrite.o libcli/cliconnect.o \ + libcli/clifile.o libcli/clilist.o libcli/clitrans2.o \ + libcli/clisecdesc.o libcli/climessage.o \ + libcli/clideltree.o \ + $(LIBRAW_OBJ) + +# LIBDFS_OBJ = libcli/clidfs.o + +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 libcli/namequery_dc.o + +#LIBMSRPC_SERVER_OBJ = libcli/trust_passwd.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_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 + +# These are like they are to avoid a dependency on GNU MAKE +@LSA_DYNAMIC_YES@RPC_MODULES1 = bin/librpc_lsarpc.@SHLIBEXT@ +@NETLOG_DYNAMIC_YES@RPC_MODULES2 = bin/librpc_NETLOGON.@SHLIBEXT@ +@SAMR_DYNAMIC_YES@RPC_MODULES3 = bin/librpc_samr.@SHLIBEXT@ +@SVC_DYNAMIC_YES@RPC_MODULES4 = bin/librpc_srvsvc.@SHLIBEXT@ +@WKS_DYNAMIC_YES@RPC_MODULES5 = bin/librpc_wkssvc.@SHLIBEXT@ +@REG_DYNAMIC_YES@RPC_MODULES6 = bin/librpc_winreg.@SHLIBEXT@ +@SPOOLSS_DYNAMIC_YES@RPC_MODULES7 = bin/librpc_spoolss.@SHLIBEXT@ +@DFS_DYNAMIC_YES@RPC_MODULES8 = bin/librpc_netdfs.@SHLIBEXT@ +RPC_MODULES = $(RPC_MODULES1) $(RPC_MODULES2) $(RPC_MODULES3) $(RPC_MODULES4) \ + $(RPC_MODULES5) $(RPC_MODULES6) $(RPC_MODULES7) $(RPC_MODULES8) + +@LSA_DYNAMIC_NO@RPC_PIPE_OBJ1 = $(RPC_LSA_OBJ) +@NETLOG_DYNAMIC_NO@RPC_PIPE_OBJ2 = $(RPC_NETLOG_OBJ) +@SAMR_DYNAMIC_NO@RPC_PIPE_OBJ3 = $(RPC_SAMR_OBJ) +@SVC_DYNAMIC_NO@RPC_PIPE_OBJ4 = $(RPC_SVC_OBJ) +@WKS_DYNAMIC_NO@RPC_PIPE_OBJ5 = $(RPC_WKS_OBJ) +@REG_DYNAMIC_NO@RPC_PIPE_OBJ6 = $(RPC_REG_OBJ) +@SPOOLSS_DYNAMIC_NO@RPC_PIPE_OBJ7 = $(RPC_SPOOLSS_OBJ) +@DFS_DYNAMIC_NO@RPC_PIPE_OBJ8 = $(RPC_DFS_OBJ) +RPC_SERVER_OBJ = $(RPC_PIPE_OBJ1) $(RPC_PIPE_OBJ2) $(RPC_PIPE_OBJ3) \ + $(RPC_PIPE_OBJ4) $(RPC_PIPE_OBJ5) $(RPC_PIPE_OBJ6) $(RPC_PIPE_OBJ7) \ + $(RPC_PIPE_OBJ8) $(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 \ + $(REGOBJS_OBJ) + + +RPC_CLIENT_OBJ = rpc_client/cli_pipe.o + +#LOCKING_OBJ = locking/locking.o locking/brlock.o locking/posix.o + +PASSDB_GET_SET_OBJ = passdb/pdb_get_set.o + +PASSDB_OBJ = $(PASSDB_GET_SET_OBJ) passdb/passdb.o passdb/pdb_interface.o \ + passdb/machine_sid.o passdb/pdb_smbpasswd.o \ + passdb/pdb_tdb.o passdb/pdb_ldap.o \ + passdb/pdb_unix.o passdb/pdb_guest.o passdb/util_sam_sid.o \ + passdb/pdb_compat.o passdb/pdb_nisplus.o \ + passdb/privileges.o + +XML_OBJ = modules/xml.o +MYSQL_OBJ = modules/mysql.o +DEVEL_HELP_OBJ = modules/developer.o + +SAM_STATIC_MODULES = sam/sam_plugin.o sam/sam_skel.o sam/sam_ads.o + +SAM_OBJ = sam/account.o sam/get_set_account.o sam/get_set_group.o \ + sam/get_set_domain.o sam/interface.o $(SAM_STATIC_MODULES) + +SAMTEST_OBJ = torture/samtest.o torture/cmd_sam.o $(SAM_OBJ) $(LIB_OBJ) $(PARAM_OBJ) $(LIBSMB_OBJ) $(READLINE_OBJ) lib/util_seaccess.o $(LIBADS_OBJ) $(KRBCLIENT_OBJ) $(PASSDB_OBJ) $(SECRETS_OBJ) $(GROUPDB_OBJ) + +GROUPDB_OBJ = groupdb/mapping.o + +# passdb/smbpass.o passdb/ldap.o passdb/nispass.o + +#PROFILE_OBJ = profile/profile.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 + +PLAINTEXT_AUTH_OBJ = auth/pampass.o auth/pass_check.o + +# UNIGRP_OBJ = libcli/netlogon_unigrp.o + +AUTH_OBJ = auth/auth.o auth/auth_sam.o \ + auth/auth_unix.o auth/auth_util.o \ + auth/auth_builtin.o auth/auth_compat.o \ + $(PLAINTEXT_AUTH_OBJ) $(UNIGRP_OBJ) + +# auth/auth_server.o auth/auth_domain.o auth/auth_winbind.o auth/auth_ntlmssp.o + +MANGLE_OBJ = smbd/mangle.o smbd/mangle_hash.o smbd/mangle_map.o smbd/mangle_hash2.o + +SMBD_OBJ_MAIN = smbd/server.o + +CSM_NTVFS_MAIN = ntvfs/tank/vfs_tank.o +#we don't want these in main proto.h +CSM_NTVFS_OBJ = ntvfs/tank/csm_init.o ntvfs/tank/csm_unlink.o \ + ntvfs/tank/csm_util.o ntvfs/tank/csm_error.o ntvfs/tank/csm_lookup.o \ + ntvfs/tank/csm_blockmap.o ntvfs/tank/csm_dir.o \ + ntvfs/tank/csm_fcntl.o ntvfs/tank/csm_io.o ntvfs/tank/csm_mkdir.o \ + ntvfs/tank/csm_open.o ntvfs/tank/csm_rename.o \ + ntvfs/tank/csm_attr.o ntvfs/tank/csm_truncate.o \ + ntvfs/tank/csm_fd.o + +@STFS_ENABLED@STFS_MAIN = $(CSM_NTVFS_MAIN) +@STFS_ENABLED@STFS_OBJS = $(CSM_NTVFS_OBJ) +@STFS_ENABLED@STFS_LIBS = -L/usr/lib -L/usr/src/newport/csm/lib -Wl,"-(,-lcsm,-lcsmlinuxusp,-)" + +SMBD_NTVFS_OBJ = ntvfs/ntvfs_base.o ntvfs/ntvfs_util.o ntvfs/ntvfs_generic.o \ + ntvfs/simple/vfs_simple.o ntvfs/simple/svfs_util.o \ + ntvfs/ipc/vfs_ipc.o ntvfs/cifs/vfs_cifs.o \ + ntvfs/print/vfs_print.o + +SMBD_OBJ_SRV = smbd/connection.o \ + smbd/session.o \ + smbd/password.o smbd/conn.o \ + smbd/negprot.o smbd/request.o \ + smbd/reply.o smbd/sesssetup.o \ + smbd/trans2.o \ + lib/sysacls.o lib/server_mutex.o \ + smbd/build_options.o smbd/service.o \ + smbd/rewrite.o \ + $(SMBD_NTVFS_OBJ) $(STFS_MAIN) @SMBD_EXTRA_OBJS@ + +PROCESS_MODEL_OBJ = smbd/process.o smbd/process_model.o smbd/process_standard.o \ + smbd/process_single.o + +# lib/util_seaccess.o + +# printing/printfsp.o + +SMBD_OBJ_BASE = $(PROCESS_MODEL_OBJ) $(SMBD_OBJ_SRV) $(STFS_OBJS) \ + $(MSDFS_OBJ) $(PARAM_OBJ) \ + $(SECRETS_OBJ) \ + $(PASSDB_OBJ) \ + $(AUTH_OBJ) $(GROUPDB_OBJ) \ + $(LIB_SMBD_OBJ) $(POPT_LIB_OBJ) $(LIBSMB_OBJ) + +# $(RPC_SERVER_OBJ) $(RPC_PARSE_OBJ) $(LOCKING_OBJ) $(LIBMSRPC_OBJ) $(LIBMSRPC_SERVER_OBJ) $(LIBADS_OBJ) $(KRBCLIENT_OBJ) $(LIBADS_SERVER_OBJ) +# $(PRINTING_OBJ) $(PROFILE_OBJ) $(PRINTBACKEND_OBJ) $(QUOTAOBJS) $(OPLOCK_OBJ) $(NOTIFY_OBJ) $(REGISTRY_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_MAIN) $(SMBD_OBJ_BASE) + +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) \ + $(PROFILE_OBJ) $(LIB_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) \ + $(PROFILE_OBJ) $(LIB_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) $(PRINTING_OBJ) $(LIBSMB_OBJ) $(LOCKING_OBJ) \ + $(PARAM_OBJ) $(PASSDB_OBJ) $(SECRETS_OBJ) $(KRBCLIENT_OBJ) \ + $(LIB_OBJ) $(GROUPDB_OBJ) $(PLAINTEXT_AUTH_OBJ) + +SMBSH_OBJ = smbwrapper/smbsh.o smbwrapper/shared.o \ + $(PARAM_OBJ) $(LIB_OBJ) + +STATUS_OBJ = utils/status.o utils/rewrite.o $(LOCKING_OBJ) $(PARAM_OBJ) \ + $(PROFILE_OBJ) $(LIB_OBJ) $(POPT_LIB_OBJ) + +SMBCONTROL_OBJ = utils/smbcontrol.o $(LOCKING_OBJ) $(PARAM_OBJ) \ + $(PROFILE_OBJ) $(LIB_OBJ) utils/rewrite.o +# printing/notify.o printing/printing_db.o + +SMBTREE_OBJ = utils/smbtree.o $(LOCKING_OBJ) $(PARAM_OBJ) \ + $(PROFILE_OBJ) $(LIB_OBJ) $(LIBSMB_OBJ) \ + $(KRBCLIENT_OBJ) + +TESTPARM_OBJ = utils/testparm.o \ + $(PARAM_OBJ) $(LIB_OBJ) $(POPT_LIB_OBJ) + +TESTPRNS_OBJ = utils/testprns.o $(PARAM_OBJ) $(PRINTING_OBJ) $(LIB_OBJ) + +SMBPASSWD_OBJ = utils/smbpasswd.o $(PARAM_OBJ) $(SECRETS_OBJ) \ + $(LIBSMB_OBJ) $(PASSDB_OBJ) $(GROUPDB_OBJ)\ + $(LIB_OBJ) $(KRBCLIENT_OBJ) + +PDBEDIT_OBJ = utils/pdbedit.o $(PARAM_OBJ) $(PASSDB_OBJ) $(LIBSAMBA_OBJ) \ + $(LIB_OBJ) $(GROUPDB_OBJ) $(SECRETS_OBJ) \ + $(POPT_LIB_OBJ) + +SMBGROUPEDIT_OBJ = utils/smbgroupedit.o $(GROUPDB_OBJ) $(PARAM_OBJ) \ + $(LIBSAMBA_OBJ) $(PASSDB_OBJ) $(SECRETS_OBJ) $(LIB_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_OBJ = $(RPCCLIENT_OBJ1) \ + $(PARAM_OBJ) $(LIBSMB_OBJ) $(LIB_OBJ) \ + $(RPC_PARSE_OBJ) $(PASSDB_OBJ) $(LIBMSRPC_OBJ) \ + $(READLINE_OBJ) $(GROUPDB_OBJ) $(KRBCLIENT_OBJ) \ + $(LIBADS_OBJ) $(SECRETS_OBJ) $(POPT_LIB_OBJ) + +PAM_WINBIND_OBJ = nsswitch/pam_winbind.po nsswitch/wb_common.po lib/snprintf.po + +#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) \ + $(LIB_OBJ) + +SMBWRAPPER_OBJ1 = smbwrapper/wrapped.o + +SMBWRAPPER_OBJ = $(SMBW_OBJ) $(SMBWRAPPER_OBJ1) + +LIBSMBCLIENT_OBJ = libcli/libcliclient.o libcli/libcli_compat.o \ + libcli/libcli_cache.o $(LIB_OBJ) \ + $(LIBSMB_OBJ) $(PARAM_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 = $(LIB_OBJ) $(PARAM_OBJ) $(SECRETS_OBJ) \ + $(LIBSMB_OBJ) $(LIBMSRPC_OBJ) $(RPC_PARSE_OBJ) $(PASSDB_OBJ) \ + $(GROUPDB_OBJ) $(KRBCLIENT_OBJ) + +LIBBIGBALLOFMUD_PICOBJS = $(LIBBIGBALLOFMUD_OBJ:.o=.po) + +CLIENT_OBJ1 = client/client.o client/clitar.o libcli/raw/clirewrite.o + +CLIENT_OBJ = $(CLIENT_OBJ1) $(PARAM_OBJ) $(LIBSMB_OBJ) \ + $(LIB_OBJ) \ + $(READLINE_OBJ) $(POPT_LIB_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 + +NET_OBJ = $(NET_OBJ1) $(SECRETS_OBJ) $(LIBSMB_OBJ) $(KRBCLIENT_OBJ) \ + $(RPC_PARSE_OBJ) $(PASSDB_OBJ) $(GROUPDB_OBJ) \ + $(PARAM_OBJ) $(LIB_OBJ) \ + $(LIBMSRPC_OBJ) $(LIBMSRPC_SERVER_OBJ) \ + $(LIBADS_OBJ) $(LIBADS_SERVER_OBJ) $(POPT_LIB_OBJ) + +CUPS_OBJ = client/smbspool.o $(PARAM_OBJ) $(LIBSMB_OBJ) \ + $(LIB_OBJ) $(KRBCLIENT_OBJ) + +MOUNT_OBJ = client/smbmount.o \ + $(PARAM_OBJ) $(LIBSMB_OBJ) $(KRBCLIENT_OBJ) $(LIB_OBJ) + +MNT_OBJ = client/smbmnt.o + +UMOUNT_OBJ = client/smbumount.o + +NMBLOOKUP_OBJ = utils/nmblookup.o $(PARAM_OBJ) $(LIBNMB_OBJ) \ + $(LIB_OBJ) + + +SMBTORTURE_RAW_OBJ = torture/raw/qfsinfo.o torture/raw/qfileinfo.o torture/raw/setfileinfo.o \ + torture/raw/search.o torture/raw/close.o torture/raw/open.o torture/raw/mkdir.o \ + torture/raw/oplock.o torture/raw/notify.o torture/raw/mux.o torture/raw/ioctl.o \ + torture/raw/chkpath.o torture/raw/unlink.o torture/raw/read.o torture/raw/context.o \ + torture/raw/write.o torture/raw/lock.o torture/raw/rename.o torture/raw/seek.o + +SMBTORTURE_OBJ1 = torture/torture.o torture/torture_util.o torture/nbio.o torture/scanner.o \ + torture/utable.o torture/denytest.o torture/mangle_test.o \ + torture/aliases.o libcli/raw/clirewrite.o $(SMBTORTURE_RAW_OBJ) + +SMBTORTURE_OBJ = $(SMBTORTURE_OBJ1) \ + $(LIBSMB_OBJ) $(LIBDFS_OBJ) $(PARAM_OBJ) $(LIB_OBJ) + + +MASKTEST_OBJ = torture/masktest.o $(LIBSMB_OBJ) $(PARAM_OBJ) \ + $(LIB_OBJ) libcli/raw/clirewrite.o + +MSGTEST_OBJ = torture/msgtest.o $(LIBSMB_OBJ) $(KRBCLIENT_OBJ) $(PARAM_OBJ) \ + $(LIB_OBJ) + +LOCKTEST_OBJ = torture/locktest.o $(LOCKING_OBJ) $(LIBSMB_OBJ) $(PARAM_OBJ) \ + $(LIB_OBJ) libcli/raw/clirewrite.o + +GENTEST_OBJ = torture/gentest.o torture/torture_util.o $(LOCKING_OBJ) $(LIBSMB_OBJ) $(PARAM_OBJ) \ + $(LIB_OBJ) libcli/raw/clirewrite.o + +NSSTEST_OBJ = torture/nsstest.o $(LIBSMB_OBJ) $(KRBCLIENT_OBJ) $(PARAM_OBJ) \ + $(LIB_OBJ) + +VFSTEST_OBJ = torture/cmd_vfs.o torture/vfstest.o $(READLINE_OBJ) + +VFS_AUDIT_OBJ = modules/vfs_audit.o +VFS_EXTD_AUDIT_OBJ = modules/vfs_extd_audit.o +VFS_RECYCLE_OBJ = modules/vfs_recycle.o +VFS_NETATALK_OBJ = modules/vfs_netatalk.o +VFS_FAKE_PERMS_OBJ = modules/vfs_fake_perms.o + +LOCKTEST2_OBJ = torture/locktest2.o $(LOCKING_OBJ) $(LIBSMB_OBJ) \ + $(KRBCLIENT_OBJ) $(PARAM_OBJ) \ + $(LIB_OBJ) + +SMBCACLS_OBJ = utils/smbcacls.o $(LOCKING_OBJ) $(LIBSMB_OBJ) $(KRBCLIENT_OBJ) \ + $(PARAM_OBJ) \ + $(LIB_OBJ) $(RPC_PARSE_OBJ) $(PASSDB_GET_SET_OBJ) \ + $(LIBMSRPC_OBJ) $(SECRETS_OBJ) + +TALLOCTORT_OBJ = lib/talloctort.o $(LIB_OBJ) $(PARAM_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) $(LIB_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 $(LIBSMB_OBJ) $(PARAM_OBJ) \ + $(LIB_OBJ) $(KRBCLIENT_OBJ) + +PROTO_OBJ = $(SMBD_OBJ_SRV) \ + $(SMBD_OBJ_MAIN) $(PROCESS_MODEL_OBJ) \ + $(NMBD_OBJ1) $(SWAT_OBJ1) $(LIBSMB_OBJ) \ + $(LIBRAW_OBJ) $(LIBDFS_OBJ) $(LIBCLIUTIL) $(LIBNTLMSSP_OBJ) \ + $(SMBW_OBJ1) $(SMBWRAPPER_OBJ1) $(SMBTORTURE_OBJ1) $(RPCCLIENT_OBJ1) \ + $(LIBMSRPC_OBJ) $(LIBMSRPC_SERVER_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) \ + $(QUOTAOBJS) $(PASSDB_OBJ) $(GROUPDB_OBJ) $(MSDFS_OBJ) \ + $(READLINE_OBJ) $(PROFILE_OBJ) $(LIBADS_OBJ) $(LIBADS_SERVER_OBJ) \ + $(LIB_SMBD_OBJ) $(SAM_OBJ) $(REGISTRY_OBJ) $(POPT_LIB_OBJ) \ + $(RPC_LSA_OBJ) $(RPC_NETLOG_OBJ) $(RPC_SAMR_OBJ) $(RPC_REG_OBJ) \ + $(RPC_SVC_OBJ) $(RPC_WKS_OBJ) $(RPC_DFS_OBJ) $(RPC_SPOOLSS_OBJ) + +NSS_OBJ_0 = nsswitch/wins.o $(PARAM_OBJ) $(LIBSMB_OBJ) \ + $(LIB_OBJ) $(NSSWINS_OBJ) + +NSS_OBJ = $(NSS_OBJ_0:.o=.po) + +PICOBJS = $(SMBWRAPPER_OBJ:.o=.po) +PICOBJS32 = $(SMBWRAPPER_OBJ:.o=.po32) +LIBSMBCLIENT_PICOBJS = $(LIBSMBCLIENT_OBJ:.o=.po) + +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 \ + libcli/smbencrypt.o libcli/smbdes.o libcli/nterr.o \ + $(PARAM_OBJ) $(LIB_OBJ) $(PASSDB_OBJ) $(GROUPDB_OBJ) \ + $(SECRETS_OBJ) + +PAM_SMBPASS_PICOOBJ = $(PAM_SMBPASS_OBJ_0:.o=.po) + +WINBINDD_OBJ1 = \ + nsswitch/winbindd.o \ + nsswitch/winbindd_user.o \ + nsswitch/winbindd_group.o \ + nsswitch/winbindd_idmap.o \ + nsswitch/winbindd_idmap_tdb.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_dual.o + +WINBINDD_OBJ = \ + $(WINBINDD_OBJ1) $(PASSDB_GET_SET_OBJ) \ + $(PARAM_OBJ) $(LIB_OBJ) \ + $(LIBSMB_OBJ) $(LIBMSRPC_OBJ) $(RPC_PARSE_OBJ) \ + $(PROFILE_OBJ) $(UNIGRP_OBJ) \ + $(SECRETS_OBJ) $(LIBADS_OBJ) $(KRBCLIENT_OBJ) + +WBINFO_OBJ = nsswitch/wbinfo.o libcli/smbencrypt.o libcli/smbdes.o $(POPT_LIB_OBJ) + +WINBIND_NSS_OBJ = nsswitch/winbind_nss.o nsswitch/wb_common.o @WINBIND_NSS_EXTRA_OBJS@ + +WINBIND_NSS_PICOBJS = $(WINBIND_NSS_OBJ:.o=.po) + +POPT_OBJS=popt/findme.o popt/popt.o popt/poptconfig.o \ + popt/popthelp.o popt/poptparse.o + +TDBBACKUP_OBJ = tdb/tdbbackup.o $(TDBBASE_OBJ) + +NTLM_AUTH_OBJ = utils/ntlm_auth.o $(LIBNTLMSSP_OBJ) $(LIBSAMBA_OBJ) $(POPT_LIB_OBJ) + +###################################################################### +# now the rules... +###################################################################### +all: bin/smbd bin/smbclient bin/smbtorture bin/locktest bin/masktest bin/gentest + +#SHOWFLAGS proto_exists $(SBIN_PROGS) $(BIN_PROGS) $(SHLIBS) \ +# $(TORTURE_PROGS) $(RPC_MODULES) @EXTRA_ALL_TARGETS@ + +pam_smbpass : SHOWFLAGS bin/pam_smbpass.@SHLIBEXT@ + +smbwrapper : SHOWFLAGS @SMBWRAPPER@ + +torture : SHOWFLAGS $(TORTURE_PROGS) + +smbtorture : SHOWFLAGS bin/smbtorture@EXEEXT@ + +gentest: SHOWFLAGS bin/gentest@EXEEXT@ + +masktest : SHOWFLAGS bin/masktest@EXEEXT@ + +msgtest : SHOWFLAGS bin/msgtest@EXEEXT@ + +locktest : SHOWFLAGS bin/locktest@EXEEXT@ + +smbcacls : SHOWFLAGS bin/smbcacls@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@ nsswitch/libnss_winbind.@SHLIBEXT@ nsswitch/pam_winbind.@SHLIBEXT@ + +wins : SHOWFLAGS nsswitch/libnss_wins.@SHLIBEXT@ + +modules: SHOWFLAGS proto_exists $(VFS_MODULES) $(PDB_MODULES) $(MODULES) + +everything: all libsmbclient debug2html smbfilter talloctort + +.SUFFIXES: +.SUFFIXES: .c .o .po .po32 .lo + +SHOWFLAGS: + @echo "Using FLAGS = $(FLAGS)" + @echo " FLAGS32 = $(FLAGS32)" + @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'` $@ + +# 'make pch' is extremely useful for fast compiles if you have gcc-3.4 +pch: + $(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.po: dynconfig.c Makefile + @if (: >> $@ || : > $@) >/dev/null 2>&1; then rm -f $@; else \ + dir=`echo $@ | sed 's,/[^/]*$$,,;s,^$$,.,'` $(MAKEDIR); fi + @echo Compiling $*.c with @PICFLAG@ + @$(CC) -I. -I$(srcdir) $(FLAGS) $(PATH_FLAGS) @PICFLAG@ -c $< -o $*.@PICSUFFIX@ +@BROKEN_CC@ -mv `echo $@ | sed -e 's%^.*/%%g' -e 's%\.po$$%.o%'` $@ +@POBAD_CC@ @mv $*.po.o $@ + +.c.po: + @if (: >> $@ || : > $@) >/dev/null 2>&1; then rm -f $@; else \ + dir=`echo $@ | sed 's,/[^/]*$$,,;s,^$$,.,'` $(MAKEDIR); fi + @echo Compiling $*.c with @PICFLAG@ + @$(CC) -I. -I$(srcdir) $(FLAGS) @PICFLAG@ -c $< -o $*.@PICSUFFIX@ +@BROKEN_CC@ -mv `echo $@ | sed -e 's%^.*/%%g' -e 's%\.po$$%.o%'` $@ +@POBAD_CC@ @mv $*.po.o $@ + +# this is for IRIX +.c.po32: + @if (: >> $@ || : > $@) >/dev/null 2>&1; then rm -f $@; else \ + dir=`echo $@ | sed 's,/[^/]*$$,,;s,^$$,.,'` $(MAKEDIR); fi + @echo Compiling $*.c with @PICFLAG@ and -32 + @$(CC) -32 -I. -I$(srcdir) $(FLAGS32) $(PATH_FLAGS) @PICFLAG@ -c $< \ + -o $*.po32.o +@BROKEN_CC@ -mv `echo $@ | sed -e 's%^.*/%%g' -e 's%\.po32$$%.o%'` $@.o + @mv $*.po32.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) $(DYNEXP) $(PRINTLIBS) \ + $(AUTHLIBS) $(ACLLIBS) $(LIBS) $(PTHREAD_LIB) @SMBD_EXTRA_LIBS@ $(STFS_LIBS) @BUILD_POPT@ + +bin/nmbd@EXEEXT@: $(NMBD_OBJ) @BUILD_POPT@ bin/.dummy + @echo Linking $@ + @$(CC) $(FLAGS) -o $@ $(NMBD_OBJ) $(LDFLAGS) $(LIBS) @BUILD_POPT@ + +bin/wrepld@EXEEXT@: $(WREPL_OBJ) bin/.dummy + @echo Linking $@ + @$(CC) $(FLAGS) -o $@ $(WREPL_OBJ) $(LDFLAGS) $(LIBS) + +bin/swat@EXEEXT@: $(SWAT_OBJ) bin/.dummy + @echo Linking $@ + @$(CC) $(FLAGS) -o $@ $(SWAT_OBJ) $(LDFLAGS) $(DYNEXP) $(PRINTLIBS) \ + $(AUTHLIBS) $(LIBS) + +bin/rpcclient@EXEEXT@: $(RPCCLIENT_OBJ) @BUILD_POPT@ bin/.dummy + @echo Linking $@ + @$(CC) $(FLAGS) -o $@ $(RPCCLIENT_OBJ) $(LDFLAGS) $(DYNEXP) $(TERMLDFLAGS) $(TERMLIBS) $(LIBS) @BUILD_POPT@ + +bin/smbclient@EXEEXT@: $(CLIENT_OBJ) @BUILD_POPT@ bin/.dummy + @echo Linking $@ + @$(CC) $(FLAGS) -o $@ $(CLIENT_OBJ) $(LDFLAGS) $(TERMLDFLAGS) $(TERMLIBS) $(LIBS) @BUILD_POPT@ + +bin/net@EXEEXT@: $(NET_OBJ) @BUILD_POPT@ bin/.dummy + @echo Linking $@ + @$(CC) $(FLAGS) -o $@ $(NET_OBJ) $(DYNEXP) $(LDFLAGS) $(LIBS) @BUILD_POPT@ + +bin/profiles@EXEEXT@: utils/profiles.o bin/.dummy + @echo Linking $@ + @$(CC) $(FLAGS) -o $@ utils/profiles.o $(LDFLAGS) $(LIBS) + +bin/editreg@EXEEXT@: utils/editreg.o bin/.dummy + @echo Linking $@ + @$(CC) $(FLAGS) -o $@ utils/editreg.o $(LDFLAGS) $(LIBS) + +bin/smbspool@EXEEXT@: $(CUPS_OBJ) bin/.dummy + @echo Linking $@ + @$(CC) $(FLAGS) -o $@ $(CUPS_OBJ) $(LDFLAGS) $(LIBS) + +bin/smbmount@EXEEXT@: $(MOUNT_OBJ) bin/.dummy + @echo Linking $@ + @$(CC) $(FLAGS) -o $@ $(MOUNT_OBJ) $(LDFLAGS) $(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) $(LIBS) @BUILD_POPT@ + +bin/testprns@EXEEXT@: $(TESTPRNS_OBJ) bin/.dummy + @echo Linking $@ + @$(CC) $(FLAGS) -o $@ $(TESTPRNS_OBJ) $(LDFLAGS) $(PRINTLIBS) $(LIBS) + +bin/smbstatus@EXEEXT@: $(STATUS_OBJ) @BUILD_POPT@ bin/.dummy + @echo Linking $@ + @$(CC) $(FLAGS) -o $@ $(STATUS_OBJ) $(LDFLAGS) $(LIBS) @BUILD_POPT@ + +bin/smbcontrol@EXEEXT@: $(SMBCONTROL_OBJ) bin/.dummy + @echo Linking $@ + @$(CC) -DUSING_SMBCONTROL $(FLAGS) -o $@ $(SMBCONTROL_OBJ) $(LDFLAGS) $(LIBS) + +bin/smbtree@EXEEXT@: $(SMBTREE_OBJ) bin/.dummy + @echo Linking $@ + @$(CC) $(FLAGS) -o $@ $(SMBTREE_OBJ) $(LDFLAGS) $(LIBS) + +bin/smbpasswd@EXEEXT@: $(SMBPASSWD_OBJ) bin/.dummy + @echo Linking $@ + @$(CC) $(FLAGS) -o $@ $(SMBPASSWD_OBJ) $(LDFLAGS) $(DYNEXP) $(LIBS) + +bin/pdbedit@EXEEXT@: $(PDBEDIT_OBJ) @BUILD_POPT@ bin/.dummy + @echo Linking $@ + @$(CC) $(FLAGS) -o $@ $(PDBEDIT_OBJ) $(LDFLAGS) $(DYNEXP) $(LIBS) @BUILD_POPT@ + +bin/samtest@EXEEXT@: $(SAMTEST_OBJ) @BUILD_POPT@ bin/.dummy + @echo Linking $@ + @$(CC) $(FLAGS) -o $@ $(SAMTEST_OBJ) $(LDFLAGS) $(DYNEXP) $(TERMLDFLAGS) $(TERMLIBS) $(DYNEXP) $(LIBS) @BUILD_POPT@ + +bin/smbgroupedit@EXEEXT@: $(SMBGROUPEDIT_OBJ) bin/.dummy + @echo Linking $@ + @$(CC) $(FLAGS) -o $@ $(SMBGROUPEDIT_OBJ) $(LDFLAGS) $(LIBS) + +bin/nmblookup@EXEEXT@: $(NMBLOOKUP_OBJ) bin/.dummy + @echo Linking $@ + @$(CC) $(FLAGS) -o $@ $(NMBLOOKUP_OBJ) $(LDFLAGS) $(LIBS) + +bin/smbtorture@EXEEXT@: $(SMBTORTURE_OBJ) bin/.dummy + @echo Linking $@ + @$(CC) $(FLAGS) -o $@ $(SMBTORTURE_OBJ) $(LDFLAGS) $(LIBS) + +bin/gentest@EXEEXT@: $(GENTEST_OBJ) bin/.dummy + @echo Linking $@ + @$(CC) $(FLAGS) -o $@ $(GENTEST_OBJ) $(LDFLAGS) $(LIBS) + +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) + +bin/msgtest@EXEEXT@: $(MSGTEST_OBJ) bin/.dummy + @echo Linking $@ + @$(CC) $(FLAGS) -o $@ $(MSGTEST_OBJ) $(LDFLAGS) $(LIBS) + +bin/smbcacls@EXEEXT@: $(SMBCACLS_OBJ) bin/.dummy + @echo Linking $@ + @$(CC) $(FLAGS) -o $@ $(SMBCACLS_OBJ) $(DYNEXP) $(LDFLAGS) $(LIBS) + +bin/locktest@EXEEXT@: $(LOCKTEST_OBJ) bin/.dummy + @echo Linking $@ + @$(CC) $(FLAGS) -o $@ $(LOCKTEST_OBJ) $(LDFLAGS) $(LIBS) + +bin/nsstest@EXEEXT@: $(NSSTEST_OBJ) bin/.dummy + @echo Linking $@ + @$(CC) $(FLAGS) -o $@ $(NSSTEST_OBJ) $(LDFLAGS) $(LIBS) + +bin/vfstest@EXEEXT@: $(VFSTEST_OBJ) @BUILD_POPT@ bin/.dummy + @echo Linking $@ + @$(CC) $(FLAGS) -o $@ $(VFSTEST_OBJ) $(LDFLAGS) $(TERMLDFLAGS) $(TERMLIBS) $(DYNEXP) $(PRINTLIBS) $(AUTHLIBS) $(ACLLIBS) $(LIBS) @BUILD_POPT@ + +bin/locktest2@EXEEXT@: $(LOCKTEST2_OBJ) bin/.dummy + @echo Linking $@ + @$(CC) $(FLAGS) -o $@ $(LOCKTEST2_OBJ) $(LDFLAGS) $(LIBS) + +bin/rpctorture@EXEEXT@: $(RPCTORTURE_OBJ) bin/.dummy + @echo Linking $@ + @$(CC) $(FLAGS) -o $@ $(RPCTORTURE_OBJ) $(DYNEXP) $(LDFLAGS) $(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) + +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) + +bin/smbsh@EXEEXT@: $(SMBSH_OBJ) bin/.dummy + @echo Linking $@ + @$(CC) $(FLAGS) -o $@ $(SMBSH_OBJ) $(LDFLAGS) $(LIBS) + +bin/smbwrapper.@SHLIBEXT@: $(PICOBJS) bin/.dummy + @echo Linking shared library $@ + @$(SHLD) $(LDSHFLAGS) -o $@ $(PICOBJS) $(LIBS) \ + @SONAMEFLAG@`basename $@` + +bin/smbwrapper.32.@SHLIBEXT@: $(PICOBJS32) + @echo Linking shared library $@ + @$(SHLD) -32 $(LDSHFLAGS) -o $@ $(PICOBJS32) $(LIBS) \ + @SONAMEFLAG@`basename $@` + +bin/libsmbclient.@SHLIBEXT@: $(LIBSMBCLIENT_PICOBJS) + @echo Linking libsmbclient shared library $@ + $(SHLD) $(LDSHFLAGS) -o $@ $(LIBSMBCLIENT_PICOBJS) $(LDFLAGS) $(LIBS) \ + @SONAMEFLAG@`basename $@`.$(LIBSMBCLIENT_MAJOR) + +bin/libsmbclient.a: $(LIBSMBCLIENT_PICOBJS) + @echo Linking libsmbclient non-shared library $@ + -$(AR) -rc $@ $(LIBSMBCLIENT_PICOBJS) + +bin/libbigballofmud.@SHLIBEXT@: $(LIBBIGBALLOFMUD_PICOBJS) + @echo Linking bigballofmud shared library $@ + $(SHLD) $(LDSHFLAGS) -o $@ $(LIBBIGBALLOFMUD_PICOBJS) $(LIBS) \ + @SONAMEFLAG@`basename $@`.$(LIBBIGBALLOFMUD_MAJOR) + +libsmbclient: bin/libsmbclient.a @LIBSMBCLIENT_SHARED@ + +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_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 $@` + +nsswitch/libnss_wins.@SHLIBEXT@: $(NSS_OBJ) + @echo "Linking $@" + @$(SHLD) $(LDSHFLAGS) -o $@ $(NSS_OBJ) -lc \ + @SONAMEFLAG@`basename $@` + +bin/winbindd@EXEEXT@: $(WINBINDD_OBJ) bin/.dummy + @echo Linking $@ + @$(LINK) -o $@ $(WINBINDD_OBJ) $(DYNEXP) $(LIBS) + +nsswitch/libns_winbind.@SHLIBEXT@: $(WINBIND_NSS_PICOBJS) + @echo "Linking $@" + @$(SHLD) @LDSHFLAGS@ -o $@ $(WINBIND_NSS_PICOBJS) @WINBIND_NSS_EXTRA_LIBS@ \ + @SONAMEFLAG@`basename $@` + +nsswitch/libnss_winbind.@SHLIBEXT@: $(WINBIND_NSS_PICOBJS) + @echo "Linking $@" + @$(SHLD) $(LDSHFLAGS) -o $@ $(WINBIND_NSS_PICOBJS) @WINBIND_NSS_EXTRA_LIBS@ \ + @SONAMEFLAG@`basename $@` + +nsswitch/pam_winbind.@SHLIBEXT@: $(PAM_WINBIND_OBJ) bin/.dummy + @echo Linking $@ + @$(SHLD) $(LDSHFLAGS) -o $@ $(PAM_WINBIND_OBJ) \ + @SONAMEFLAG@`basename $@` -lpam + +bin/mysql.@SHLIBEXT@: $(MYSQL_OBJ) + @echo "Building plugin $@" + @$(SHLD) $(LDSHFLAGS) -o $@ $(MYSQL_OBJ) @MYSQL_LIBS@ \ + @SONAMEFLAG@`basename $@` + +bin/developer.@SHLIBEXT@: $(DEVEL_HELP_OBJ) + @echo "Building plugin $@" + @$(SHLD) $(LDSHFLAGS) -o $@ $(DEVEL_HELP_OBJ) \ + @SONAMEFLAG@`basename $@` + +bin/xml.@SHLIBEXT@: $(XML_OBJ) + @echo "Building plugin $@" + @$(SHLD) $(LDSHFLAGS) -o $@ $(XML_OBJ) @XML_LIBS@ \ + @SONAMEFLAG@`basename $@` + +bin/vfs_audit.@SHLIBEXT@: $(VFS_AUDIT_OBJ) + @echo "Building plugin $@" + @$(SHLD) $(LDSHFLAGS) -o $@ $(VFS_AUDIT_OBJ) \ + @SONAMEFLAG@`basename $@` + +bin/vfs_extd_audit.@SHLIBEXT@: $(VFS_EXTD_AUDIT_OBJ) + @echo "Building plugin $@" + @$(SHLD) $(LDSHFLAGS) -o $@ $(VFS_AUDIT_OBJ) \ + @SONAMEFLAG@`basename $@` + +bin/vfs_recycle.@SHLIBEXT@: $(VFS_RECYCLE_OBJ) + @echo "Building plugin $@" + @$(SHLD) $(LDSHFLAGS) -o $@ $(VFS_RECYCLE_OBJ) \ + @SONAMEFLAG@`basename $@` + +bin/vfs_netatalk.@SHLIBEXT@: $(VFS_NETATALK_OBJ) + @echo "Building plugin $@" + @$(SHLD) $(LDSHFLAGS) -o $@ $(VFS_NETATALK_OBJ) \ + @SONAMEFLAG@`basename $@` + +bin/vfs_fake_perms.@SHLIBEXT@: $(VFS_FAKE_PERMS_OBJ) + @echo "Building plugin $@" + @$(SHLD) $(LDSHFLAGS) -o $@ $(VFS_FAKE_PERMS_OBJ) \ + @SONAMEFLAG@`basename $@` + +bin/wbinfo@EXEEXT@: $(WBINFO_OBJ) $(PARAM_OBJ) $(LIB_OBJ) \ + $(SECRETS_OBJ) @BUILD_POPT@ bin/.dummy + @echo Linking $@ + @$(LINK) -o $@ $(WBINFO_OBJ) $(PARAM_OBJ) $(LIB_OBJ) \ + $(SECRETS_OBJ) $(LIBS) @BUILD_POPT@ + +bin/ntlm_auth@EXEEXT@: $(NTLM_AUTH_OBJ) $(PARAM_OBJ) $(LIB_OBJ) \ + @BUILD_POPT@ bin/.dummy + @echo Linking $@ + @$(LINK) -o $@ $(NTLM_AUTH_OBJ) $(PARAM_OBJ) $(LIB_OBJ) \ + $(LIBS) @BUILD_POPT@ + +bin/pam_smbpass.@SHLIBEXT@: $(PAM_SMBPASS_PICOOBJ) + @echo "Linking shared library $@" + $(SHLD) $(LDSHFLAGS) -o $@ $(PAM_SMBPASS_PICOOBJ) -lpam $(DYNEXP) $(LIBS) -lc + +bin/libmsrpc.a: $(LIBMSRPC_PICOBJ) + -$(AR) -rc $@ $(LIBMSRPC_PICOBJ) + +bin/tdbbackup@EXEEXT@: $(TDBBACKUP_OBJ) bin/.dummy + @echo Linking $@ + @$(CC) $(FLAGS) -o $@ $(TDBBACKUP_OBJ) + +install: installbin installman installscripts installdat installswat + +# 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)$(VFSLIBDIR) $(DESTDIR)$(PDBLIBDIR) $(DESTDIR)$(PIDDIR) $(DESTDIR)$(LOCKDIR) + +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)$(BASEDIR) $(DESTDIR)$(SBINDIR) $(DESTDIR)$(LIBDIR) $(DESTDIR)$(VARDIR) $(SBIN_PROGS) + @$(SHELL) $(srcdir)/script/installbin.sh $(INSTALLPERMS) $(DESTDIR)$(BASEDIR) $(DESTDIR)$(BINDIR) $(DESTDIR)$(LIBDIR) $(DESTDIR)$(VARDIR) $(BIN_PROGS) + + @$(SHELL) $(srcdir)/script/installmodules.sh $(INSTALLPERMS) $(DESTDIR)$(BASEDIR) $(DESTDIR)$(RPCLIBDIR) $(RPC_MODULES) + +installmodules: all 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) + +installscripts: installdirs + @$(SHELL) $(srcdir)/script/installscripts.sh $(INSTALLPERMS) $(DESTDIR)$(BINDIR) $(SCRIPTS) + +installdat: installdirs + @$(SHELL) $(srcdir)/script/installdat.sh $(DESTDIR)$(LIBDIR) $(srcdir) + +installswat: installdirs + @$(SHELL) $(srcdir)/script/installswat.sh $(DESTDIR)$(SWATDIR) $(srcdir) + +installclientlib: + -$(INSTALLCLIENTCMD_SH) bin/libsmbclient.@SHLIBEXT@ $(DESTDIR)${prefix}/lib + -$(INSTALLCLIENTCMD_A) bin/libsmbclient.a $(DESTDIR)${prefix}/lib + -$(INSTALLCMD) -d $(DESTDIR)${prefix}/include + -$(INSTALLCMD) include/libsmbclient.h $(DESTDIR)${prefix}/include + +# Python extensions + +PYTHON_OBJS = $(LIB_OBJ) $(LIBSMB_OBJ) $(RPC_PARSE_OBJ) \ + $(PARAM_OBJ) $(LIBMSRPC_OBJ) $(PASSDB_OBJ) $(GROUPDB_OBJ) \ + $(SECRETS_OBJ) $(KRBCLIENT_OBJ) + +python_ext: $(PYTHON_OBJS) + @if test -z "$(PYTHON)"; then \ + echo Use the option --with-python to configure python; \ + exit 1; fi + PYTHON_OBJS="$(PYTHON_OBJS)" PYTHON_CFLAGS="$(CFLAGS) $(CPPFLAGS) $(FLAGS)" \ + LIBS="$(LIBS)" \ + $(PYTHON) python/setup.py build + +python_install: $(PYTHON_OBJS) + @if test -z "$(PYTHON)"; then \ + echo Use the option --with-python to configure python; \ + exit 1; fi + PYTHON_OBJS="$(PYTHON_OBJS)" PYTHON_CFLAGS="$(CFLAGS) $(CPPFLAGS)" \ + LIBS="$(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: + @$(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)" + + +uninstall: uninstallman uninstallbin uninstallscripts + +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) + @$(SHELL) $(srcdir)/script/uninstallmodules.sh $(INSTALLPERMS) $(DESTDIR)$(BASEDIR) $(DESTDIR)$(RPCLIBDIR) $(DESTDIR)$(RPC_MODULES) + +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)$(LIBDIR) $(DESTDIR)$(MODULES) + +uninstallscripts: + @$(SHELL) $(srcdir)/script/uninstallscripts.sh $(INSTALLPERMS) $(DESTDIR)$(BINDIR) $(SCRIPTS) + +# Toplevel clean files +TOPFILES=dynconfig.o dynconfig.po + +clean: delheaders python_clean + -rm -f core */*~ *~ */*.o */*/*.o */*/*.po */*/*.po32 */*.po */*.po32 */*.@SHLIBEXT@ \ + $(TOPFILES) $(BIN_PROGS) $(SBIN_PROGS) $(MODULES) $(TORTURE_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 \ +@STFS_ENABLED@ ntvfs/tank/vfs_tank_proto.h \ + client/client_proto.h utils/net_proto.h \ + include/tdbsam2_parse_info.h + +delheaders: + @echo Removing prototype headers + @/bin/rm -f $(srcdir)/include/proto.h $(srcdir)/include/build_env.h + @/bin/rm -f $(srcdir)/include/wrepld_proto.h $(srcdir)/nsswitch/winbindd_proto.h + @/bin/rm -f $(srcdir)/web/swat_proto.h + @/bin/rm -f $(srcdir)/client/client_proto.h $(srcdir)/utils/net_proto.h + @/bin/rm -f $(srcdir)/include/tdbsam2_parse_info.h +@STFS_ENABLED@ @/bin/rm -f $(srcdir)/ntvfs/tank/vfs_tank_proto.h + +include/proto.h: + @echo Building include/proto.h + @cd $(srcdir) && $(SHELL) script/mkproto.sh $(AWK) \ + -h _PROTO_H_ $(builddir)/include/proto.h \ + $(PROTO_OBJ) + +include/build_env.h: + @echo Building include/build_env.h + @cd $(srcdir) && $(SHELL) 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) script/mkproto.sh $(AWK) \ + -h _WREPLD_PROTO_H_ $(builddir)/include/wrepld_proto.h \ + $(WREPL_OBJ1) + +nsswitch/winbindd_proto.h: + @cd $(srcdir) && $(SHELL) script/mkproto.sh $(AWK) \ + -h _WINBINDD_PROTO_H_ nsswitch/winbindd_proto.h \ + $(WINBINDD_OBJ1) + +ntvfs/tank/vfs_tank_proto.h: + @cd $(srcdir) && $(SHELL) script/mkproto.sh $(AWK) \ + -h _VFS_TANK_PROTO_H_ ntvfs/tank/vfs_tank_proto.h \ + $(STFS_OBJS) + +web/swat_proto.h: + @cd $(srcdir) && $(SHELL) script/mkproto.sh $(AWK) \ + -h _SWAT_PROTO_H_ web/swat_proto.h \ + $(SWAT_OBJ1) + +client/client_proto.h: + @cd $(srcdir) && $(SHELL) script/mkproto.sh $(AWK) \ + -h _CLIENT_PROTO_H_ client/client_proto.h \ + $(CLIENT_OBJ1) + +utils/net_proto.h: + @cd $(srcdir) && $(SHELL) script/mkproto.sh $(AWK) \ + -h _CLIENT_PROTO_H_ utils/net_proto.h \ + $(NET_OBJ1) + +include/tdbsam2_parse_info.h: + @cd $(srcdir) && @PERL@ -w script/genstruct.pl \ + -o include/tdbsam2_parse_info.h $(CC) -E -g \ + include/tdbsam2.h + +# "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) 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; \ + $(MAKE) include/tdbsam2_parse_info.h; \ +@STFS_ENABLED@ $(MAKE) ntvfs/tank/vfs_tank_proto.h + +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_PROGS) $(MODULES) $(SBIN_PROGS) 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 + +# 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 autoconf" + +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" + +test_prefix=/tmp/test-samba +# Run regression suite using the external "satyr" framework +check: + @echo "** Sorry, samba self-test without installation does not work " + @echo "** yet. Please try specifying a scratch directory to" + @echo "** ./configure --prefix DIR" + @echo "** then run \"make install installcheck\"" + exit 1 + +# -rm -rf $(test_prefix)/lib +# mkdir $(test_prefix)/lib -p ./testdir +# PATH=$(builddir)/bin:$(PATH) \ +# SATYR_SUITEDIR=../testsuite/build_farm/ prefix=$(test_prefix) \ +# testdir=./testdir $(SHELL) satyr + +# Run regression suite on the installed version. + +# `installcheck' +# Perform installation tests (if any). The user must build and +# install the program before running the tests. You should not +# assume that `$(bindir)' is in the search path. + +dangerous-installcheck: + mkdir -p $(BASEDIR)/lib + mkdir -p $(BASEDIR)/var + PATH=$(BINDIR):$(SBINDIR):$(PATH) \ + SATYR_DISCOURAGE=1 \ + SATYR_SUITEDIR=../testsuite/satyr/ prefix=$(BASEDIR) \ + LIBSMB_PROG=$(SBINDIR)/smbd \ + testdir=./testdir $(SHELL) satyr diff --git a/source4/aclocal.m4 b/source4/aclocal.m4 new file mode 100644 index 0000000000..7bec88dd87 --- /dev/null +++ b/source4/aclocal.m4 @@ -0,0 +1,643 @@ +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 +#include +#include ], [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 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 +]) + +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 &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 +#include +#include +#include + +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, µ) != 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 +#include +], [ 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="") +AC_ARG_ENABLE(mysqltest, [ --disable-mysqltest Do not try to compile and run a test MYSQL program], + , enable_mysqltest=yes) + + 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) + min_mysql_version=ifelse([$1], ,0.11.0,$1) + AC_MSG_CHECKING(for MYSQL - version >= $min_mysql_version) + no_mysql="" + if test "$MYSQL_CONFIG" = "no" ; then + no_mysql=yes + else + MYSQL_CFLAGS=`$MYSQL_CONFIG $mysqlconf_args --cflags | sed -e "s/'//g"` + MYSQL_LIBS=`$MYSQL_CONFIG $mysqlconf_args --libs | sed -e "s/'//g"` + + mysql_major_version=`$MYSQL_CONFIG $mysql_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` + mysql_minor_version=`$MYSQL_CONFIG $mysql_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` + mysql_micro_version=`$MYSQL_CONFIG $mysql_config_args --version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` + if test "x$enable_mysqltest" = "xyes" ; then + ac_save_CFLAGS="$CFLAGS" + ac_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $MYSQL_CFLAGS" + LIBS="$LIBS $MYSQL_LIBS" +dnl +dnl Now check if the installed MYSQL is sufficiently new. (Also sanity +dnl checks the results of mysql_config to some extent +dnl + rm -f conf.mysqltest + AC_TRY_RUN([ +#include +#include +#include +#include + +char* +my_strdup (char *str) +{ + char *new_str; + + if (str) + { + new_str = (char *)malloc ((strlen (str) + 1) * sizeof(char)); + strcpy (new_str, str); + } + else + new_str = NULL; + + return new_str; +} + +int main (int argc, char *argv[]) +{ +int major, minor, micro; + char *tmp_version; + + /* This hangs on some systems (?) + system ("touch conf.mysqltest"); + */ + { FILE *fp = fopen("conf.mysqltest", "a"); if ( fp ) fclose(fp); } + + /* HP/UX 9 (%@#!) writes to sscanf strings */ + tmp_version = my_strdup("$min_mysql_version"); + if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, µ) != 3) { + printf("%s, bad version string\n", "$min_mysql_version"); + exit(1); + } + + if (($mysql_major_version > major) || + (($mysql_major_version == major) && ($mysql_minor_version > minor)) || + (($mysql_major_version == major) && ($mysql_minor_version == minor) && ($mysql_micro_version >= micro))) + { + return 0; + } + else + { + printf("\n*** 'mysql_config --version' returned %d.%d.%d, but the minimum version\n", $mysql_major_version, $mysql_minor_version, $mysql_micro_version); + printf("*** of MYSQL required is %d.%d.%d. If mysql_config is correct, then it is\n", major, minor, micro); + printf("*** best to upgrade to the required version.\n"); + printf("*** If mysql_config was wrong, set the environment variable MYSQL_CONFIG\n"); + printf("*** to point to the correct copy of mysql_config, and remove the file\n"); + printf("*** config.cache before re-running configure\n"); + return 1; + } +} + +],, no_mysql=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"]) + CFLAGS="$ac_save_CFLAGS" + LIBS="$ac_save_LIBS" + fi + fi + if test "x$no_mysql" = x ; then + AC_MSG_RESULT(yes) + ifelse([$2], , :, [$2]) + else + AC_MSG_RESULT(no) + if test "$MYSQL_CONFIG" = "no" ; then + echo "*** The mysql_config script installed by MYSQL could not be found" + echo "*** If MYSQL was installed in PREFIX, make sure PREFIX/bin is in" + echo "*** your path, or set the MYSQL_CONFIG environment variable to the" + echo "*** full path to mysql_config." + else + if test -f conf.mysqltest ; then + : + else + echo "*** Could not run MYSQL test program, checking why..." + CFLAGS="$CFLAGS $MYSQL_CFLAGS" + LIBS="$LIBS $MYSQL_LIBS" + AC_TRY_LINK([ +#include +#include + +int main(int argc, char *argv[]) +{ return 0; } +#undef main +#define main K_and_R_C_main +], [ return 0; ], + [ echo "*** The test program compiled, but did not run. This usually means" + echo "*** that the run-time linker is not finding MYSQL or finding the wrong" + echo "*** version of MYSQL. If it is not finding MYSQL, 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 MYSQL was incorrectly installed" + echo "*** or that you have moved MYSQL since it was installed. In the latter case, you" + echo "*** may want to edit the mysql_config script: $MYSQL_CONFIG" ]) + CFLAGS="$ac_save_CFLAGS" + LIBS="$ac_save_LIBS" + fi + fi + MYSQL_CFLAGS="" + MYSQL_LIBS="" + ifelse([$3], , :, [$3]) + fi + AC_SUBST(MYSQL_CFLAGS) + AC_SUBST(MYSQL_LIBS) + rm -f conf.mysqltest +]) + +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=no + jm_cv_giconv=no + AC_TRY_LINK([#include +#include ], + [iconv_t cd = iconv_open("",""); + iconv(cd,NULL,NULL,NULL,NULL); + iconv_close(cd);], + jm_cv_func_iconv=yes + jm_cv_giconv=yes) + + if test "$jm_cv_func_iconv" != yes; then + AC_TRY_LINK([#include +#include ], + [iconv_t cd = iconv_open("",""); + iconv(cd,NULL,NULL,NULL,NULL); + iconv_close(cd);], + jm_cv_func_iconv=yes) + + if test "$jm_cv_lib_iconv" != yes; then + jm_save_LIBS="$LIBS" + LIBS="$LIBS -lgiconv" + AC_TRY_LINK([#include +#include ], + [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_giconv=yes) + LIBS="$jm_save_LIBS" + + if test "$jm_cv_func_iconv" != yes; then + jm_save_LIBS="$LIBS" + LIBS="$LIBS -liconv" + AC_TRY_LINK([#include +#include ], + [iconv_t cd = iconv_open("",""); + iconv(cd,NULL,NULL,NULL,NULL); + iconv_close(cd);], + jm_cv_lib_iconv=yes + jm_cv_func_iconv=yes) + LIBS="$jm_save_LIBS" + 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 + AC_DEFINE(HAVE_ICONV, 1, [What header to include for iconv() function: iconv.h]) + AC_MSG_RESULT(yes) + ICONV_FOUND=yes + fi + else + AC_MSG_RESULT(no) + fi + if test "$jm_cv_lib_iconv" = yes; then + if test "$jm_cv_giconv" = yes; then + LIBS="$LIBS -lgiconv" + else + LIBS="$LIBS -liconv" + fi + fi +]) + +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)]) diff --git a/source4/auth/auth.c b/source4/auth/auth.c new file mode 100644 index 0000000000..74c60f6a95 --- /dev/null +++ b/source4/auth/auth.c @@ -0,0 +1,456 @@ +/* + 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 + +/** List of various built-in authentication modules */ + +static const struct auth_init_function_entry builtin_auth_init_functions[] = { + { "guest", auth_init_guest }, +// { "rhosts", auth_init_rhosts }, +// { "hostsequiv", auth_init_hostsequiv }, + { "sam", auth_init_sam }, + { "samstrict", auth_init_samstrict }, + { "samstrict_dc", auth_init_samstrict_dc }, + { "unix", auth_init_unix }, +// { "smbserver", auth_init_smbserver }, +// { "ntdomain", auth_init_ntdomain }, +// { "trustdomain", auth_init_trustdomain }, +// { "winbind", auth_init_winbind }, +#ifdef DEVELOPER + { "name_to_ntstatus", auth_init_name_to_ntstatus }, + { "fixed_challenge", auth_init_fixed_challenge }, +#endif + { "plugin", auth_init_plugin }, + { NULL, 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; + } + + 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"; + } + + DEBUG(5, ("auth_context challenge created by %s\n", challenge_set_by)); + DEBUG(5, ("challenge is: \n")); + dump_data(5, 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) +{ + + NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE; + const char *pdb_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, 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) { + mem_ctx = talloc_init("%s authentication for user %s\\%s", auth_method->name, + user_info->domain.str, user_info->smb_name.str); + + nt_status = auth_method->auth(auth_context, auth_method->private_data, mem_ctx, user_info, server_info); + if (NT_STATUS_IS_OK(nt_status)) { + DEBUG(3, ("check_ntlm_password: %s authentication for user [%s] suceeded\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; + } + + /* This is one of the few places the *relies* (rather than just sets defaults + on the value of lp_security(). This needs to change. A new paramater + perhaps? */ + if (lp_security() >= SEC_SERVER) + smb_user_control(user_info, *server_info, nt_status); + + if (NT_STATUS_IS_OK(nt_status)) { + pdb_username = pdb_get_username((*server_info)->sam_account); + if (!(*server_info)->guest) { + /* We might not be root if we are an RPC call */ + become_root(); + nt_status = smb_pam_accountcheck(pdb_username); + unbecome_root(); + + if (NT_STATUS_IS_OK(nt_status)) { + DEBUG(5, ("check_ntlm_password: PAM Account for user [%s] suceeded\n", + pdb_username)); + } else { + DEBUG(3, ("check_ntlm_password: PAM Account for user [%s] FAILED with error %s\n", + pdb_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] suceeded\n", + (*server_info)->guest ? "guest " : "", + user_info->smb_name.str, + user_info->internal_username.str, + pdb_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) +{ + if (*auth_context != 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; +} + +/*************************************************************************** + 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; + int i; + 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++) { + DEBUG(5,("make_auth_context_text_list: Attempting to find an auth method to match %s\n", + *text_list)); + for (i = 0; builtin_auth_init_functions[i].name; i++) { + char *module_name = smb_xstrdup(*text_list); + char *module_params = NULL; + char *p; + + p = strchr(module_name, ':'); + if (p) { + *p = 0; + module_params = p+1; + trim_string(module_params, " ", " "); + } + + trim_string(module_name, " ", " "); + + if (strequal(builtin_auth_init_functions[i].name, module_name)) { + DEBUG(5,("make_auth_context_text_list: Found auth method %s (at pos %d)\n", *text_list, i)); + if (NT_STATUS_IS_OK(builtin_auth_init_functions[i].init(*auth_context, module_params, &t))) { + DEBUG(5,("make_auth_context_text_list: auth method %s has a valid init\n", + *text_list)); + DLIST_ADD_END(list, t, tmp); + } else { + DEBUG(0,("make_auth_context_text_list: auth method %s did not correctly init\n", + *text_list)); + } + break; + } + SAFE_FREE(module_name); + } + } + + (*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()) { + DEBUG(5,("Making default auth method list for 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 ads 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(chal, 8); + (*auth_context)->challenge_set_by = "fixed"; + return nt_status; +} + + diff --git a/source4/auth/auth_builtin.c b/source4/auth/auth_builtin.c new file mode 100644 index 0000000000..32f39311dc --- /dev/null +++ b/source4/auth/auth_builtin.c @@ -0,0 +1,210 @@ +/* + Unix SMB/CIFS implementation. + Generic authenticaion 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) +{ + NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE; + + 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 */ + +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; +} + +/** + * 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 (strncasecmp("NT_STATUS", user, strlen("NT_STATUS")) == 0) { + strupper(user); + return nt_status_string_to_code(user); + } + + strlower(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 initailisation function */ + +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 varaible 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_UNSUCCESSFUL; +} + +/**************************************************************************** + 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 */ + +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; +} + +/** + * Outsorce an auth module to an external loadable .so + * + * Only works on systems with dlopen() etc. + **/ + +/* Plugin modules initialisation */ + +NTSTATUS auth_init_plugin(struct auth_context *auth_context, const char *param, auth_methods **auth_method) +{ + void * dl_handle; + char *plugin_param, *plugin_name, *p; + auth_init_function plugin_init; + + if (param == NULL) { + DEBUG(0, ("auth_init_plugin: The plugin module needs an argument!\n")); + return NT_STATUS_UNSUCCESSFUL; + } + + plugin_name = smb_xstrdup(param); + p = strchr(plugin_name, ':'); + if (p) { + *p = 0; + plugin_param = p+1; + trim_string(plugin_param, " ", " "); + } else plugin_param = NULL; + + trim_string(plugin_name, " ", " "); + + DEBUG(5, ("auth_init_plugin: Trying to load auth plugin %s\n", plugin_name)); + dl_handle = sys_dlopen(plugin_name, RTLD_NOW ); + if (!dl_handle) { + DEBUG(0, ("auth_init_plugin: Failed to load auth plugin %s using sys_dlopen (%s)\n", + plugin_name, sys_dlerror())); + return NT_STATUS_UNSUCCESSFUL; + } + + plugin_init = sys_dlsym(dl_handle, "auth_init"); + if (!plugin_init){ + DEBUG(0, ("Failed to find function 'auth_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 paramater %s\n", plugin_name, plugin_param?plugin_param:"(null)")); + return plugin_init(auth_context, plugin_param, auth_method); +} diff --git a/source4/auth/auth_compat.c b/source4/auth/auth_compat.c new file mode 100644 index 0000000000..49cd2e8468 --- /dev/null +++ b/source4/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(struct server_context *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; + 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 = smb->negotiate.auth_context->check_ntlm_password(smb->negotiate.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(struct server_context *smb, const char *smb_name, DATA_BLOB password_blob) +{ + + DATA_BLOB null_password = data_blob(NULL, 0); + BOOL encrypted = (smb->negotiate.encrypted_passwords && password_blob.length == 24); + NTSTATUS status; + + if (encrypted) { + /* + * The password could be either NTLM or plain LM. Try NTLM first, + * but fall-through as required. + * NTLMv2 makes no sense here. + */ + status = pass_check_smb(smb, smb_name, lp_workgroup(), null_password, + password_blob, null_password, encrypted); + if (NT_STATUS_IS_OK(status)) { + return True; + } + + status = pass_check_smb(smb, smb_name, lp_workgroup(), password_blob, + null_password, null_password, encrypted); + } else { + status = pass_check_smb(smb, smb_name, lp_workgroup(), null_password, + null_password, password_blob, encrypted); + } + + return NT_STATUS_IS_OK(status); +} + + diff --git a/source4/auth/auth_domain.c b/source4/auth/auth_domain.c new file mode 100644 index 0000000000..ff759539da --- /dev/null +++ b/source4/auth/auth_domain.c @@ -0,0 +1,554 @@ +/* + 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 + +BOOL global_machine_password_needs_changing = False; + +extern userdom_struct current_user_info; + + +/* + resolve the name of a DC in ways appropriate for an ADS domain mode + an ADS domain may not have Netbios enabled at all, so this is + quite different from the RPC case + Note that we ignore the 'server' parameter here. That has the effect of using + the 'ADS server' smb.conf parameter, which is what we really want anyway + */ +static NTSTATUS ads_resolve_dc(fstring remote_machine, + struct in_addr *dest_ip) +{ + ADS_STRUCT *ads; + ads = ads_init_simple(); + if (!ads) { + return NT_STATUS_NO_LOGON_SERVERS; + } + + DEBUG(4,("ads_resolve_dc: realm=%s\n", ads->config.realm)); + + ads->auth.flags |= ADS_AUTH_NO_BIND; + +#ifdef HAVE_ADS + /* a full ads_connect() is actually overkill, as we don't srictly need + to do the SASL auth in order to get the info we need, but libads + doesn't offer a better way right now */ + ads_connect(ads); +#endif + + fstrcpy(remote_machine, ads->config.ldap_server_name); + strupper(remote_machine); + *dest_ip = ads->ldap_ip; + ads_destroy(&ads); + + if (!*remote_machine || is_zero_ip(*dest_ip)) { + return NT_STATUS_NO_LOGON_SERVERS; + } + + DEBUG(4,("ads_resolve_dc: using server='%s' IP=%s\n", + remote_machine, inet_ntoa(*dest_ip))); + + return NT_STATUS_OK; +} + +/* + resolve the name of a DC in ways appropriate for RPC domain mode + this relies on the server supporting netbios and port 137 not being + firewalled + */ +static NTSTATUS rpc_resolve_dc(const char *server, + fstring remote_machine, + struct in_addr *dest_ip) +{ + if (is_ipaddress(server)) { + struct in_addr to_ip = *interpret_addr2(server); + + /* we need to know the machines netbios name - this is a lousy + way to find it, but until we have a RPC call that does this + it will have to do */ + if (!name_status_find("*", 0x20, 0x20, to_ip, remote_machine)) { + DEBUG(2, ("rpc_resolve_dc: Can't resolve name for IP %s\n", server)); + return NT_STATUS_NO_LOGON_SERVERS; + } + + *dest_ip = to_ip; + return NT_STATUS_OK; + } + + fstrcpy(remote_machine, server); + strupper(remote_machine); + if (!resolve_name(remote_machine, dest_ip, 0x20)) { + DEBUG(1,("rpc_resolve_dc: Can't resolve address for %s\n", + remote_machine)); + return NT_STATUS_NO_LOGON_SERVERS; + } + + DEBUG(4,("rpc_resolve_dc: using server='%s' IP=%s\n", + remote_machine, inet_ntoa(*dest_ip))); + + return NT_STATUS_OK; +} + +/** + * Connect to a remote server for 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 trust_passwd the trust password to establish the + * credentials with. + * + **/ + +static NTSTATUS connect_to_domain_password_server(struct cli_state **cli, + const char *server, + const char *setup_creds_as, + uint16 sec_chan, + const unsigned char *trust_passwd, + BOOL *retry) +{ + struct in_addr dest_ip; + fstring remote_machine; + NTSTATUS result; + uint32 neg_flags = 0x000001ff; + + *retry = False; + + if (lp_security() == SEC_ADS) + result = ads_resolve_dc(remote_machine, &dest_ip); + else + result = rpc_resolve_dc(server, remote_machine, &dest_ip); + + if (!NT_STATUS_IS_OK(result)) { + DEBUG(2,("connect_to_domain_password_server: unable to resolve DC: %s\n", + nt_errstr(result))); + return result; + } + + if (ismyip(dest_ip)) { + DEBUG(1,("connect_to_domain_password_server: Password server loop - not using password server %s\n", + remote_machine)); + return NT_STATUS_NO_LOGON_SERVERS; + } + + /* 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. + */ + + *retry = True; + + if (!grab_server_mutex(server)) + return NT_STATUS_NO_LOGON_SERVERS; + + /* Attempt connection */ + result = cli_full_connection(cli, lp_netbios_name(), remote_machine, + &dest_ip, 0, "IPC$", "IPC", "", "", "",0, retry); + + if (!NT_STATUS_IS_OK(result)) { + 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" . + */ + + 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", remote_machine, cli_errstr(*cli))); + cli_nt_session_close(*cli); + cli_ulogoff(*cli); + cli_shutdown(*cli); + release_server_mutex(); + return NT_STATUS_NO_LOGON_SERVERS; + } + + snprintf((*cli)->mach_acct, sizeof((*cli)->mach_acct) - 1, "%s$", setup_creds_as); + + if (!(*cli)->mach_acct) { + release_server_mutex(); + return NT_STATUS_NO_MEMORY; + } + + result = cli_nt_setup_creds(*cli, sec_chan, trust_passwd, &neg_flags, 2); + + 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", remote_machine, 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; +} + +/*********************************************************************** + Utility function to attempt a connection to an IP address of a DC. +************************************************************************/ + +static NTSTATUS attempt_connect_to_dc(struct cli_state **cli, + const char *domain, + struct in_addr *ip, + const char *setup_creds_as, + uint16 sec_chan, + const unsigned char *trust_passwd) +{ + NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; + BOOL retry = True; + fstring dc_name; + int i; + + /* + * Ignore addresses we have already tried. + */ + + if (is_zero_ip(*ip)) + return NT_STATUS_NO_LOGON_SERVERS; + + if (!lookup_dc_name(lp_netbios_name(), domain, ip, dc_name)) + return NT_STATUS_NO_LOGON_SERVERS; + + for (i = 0; (!NT_STATUS_IS_OK(ret)) && retry && (i < 3); i++) + ret = connect_to_domain_password_server(cli, dc_name, setup_creds_as, + sec_chan, trust_passwd, &retry); + return ret; +} + +/*********************************************************************** + We have been asked to dynamically determine the IP addresses of + the PDC and BDC's for DOMAIN, and query them in turn. +************************************************************************/ +static NTSTATUS find_connect_dc(struct cli_state **cli, + const char *domain, + const char *setup_creds_as, + uint16 sec_chan, + unsigned char *trust_passwd, + time_t last_change_time) +{ + struct in_addr dc_ip; + fstring srv_name; + + if ( !rpc_find_dc(lp_workgroup(), srv_name, &dc_ip) ) { + DEBUG(0,("find_connect_dc: Failed to find an DCs for %s\n", lp_workgroup())); + return NT_STATUS_NO_LOGON_SERVERS; + } + + return attempt_connect_to_dc( cli, domain, &dc_ip, setup_creds_as, + sec_chan, trust_passwd ); +} + +/*********************************************************************** + 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 *server, const char *setup_creds_as, + uint16 sec_chan, + unsigned char trust_passwd[16], + time_t last_change_time) +{ + fstring remote_machine; + NET_USER_INFO_3 info3; + struct cli_state *cli = NULL; + NTSTATUS nt_status = NT_STATUS_NO_LOGON_SERVERS; + + /* + * 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. + */ + + while (!NT_STATUS_IS_OK(nt_status) && + next_token(&server,remote_machine,LIST_SEP,sizeof(remote_machine))) { + if(lp_security() != SEC_ADS && strequal(remote_machine, "*")) { + nt_status = find_connect_dc(&cli, domain, setup_creds_as, sec_chan, trust_passwd, last_change_time); + } else { + int i; + BOOL retry = True; + for (i = 0; !NT_STATUS_IS_OK(nt_status) && retry && (i < 3); i++) + nt_status = connect_to_domain_password_server(&cli, remote_machine, 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")); + 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, + user_info->smb_name.str, user_info->domain.str, + user_info->wksta_name.str, chal, + user_info->lm_resp, user_info->nt_resp, + &info3); + + 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))); + } else { + nt_status = make_server_info_info3(mem_ctx, user_info->internal_username.str, + user_info->smb_name.str, domain, server_info, &info3); +#if 0 + /* The stuff doesn't work right yet */ + SMB_ASSERT(sizeof((*server_info)->session_key) == sizeof(info3.user_sess_key)); + memcpy((*server_info)->session_key, info3.user_sess_key, sizeof((*server_info)->session_key)/* 16 */); + SamOEMhash((*server_info)->session_key, trust_passwd, sizeof((*server_info)->session_key)); +#endif + + uni_group_cache_store_netlogon(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, remote_machine, 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); + release_server_mutex(); + 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; + char *password_server; + unsigned char trust_passwd[16]; + time_t last_change_time; + const char *domain = lp_workgroup(); + + 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(is_myname(user_info->domain.str)) { + DEBUG(3,("check_ntdomain_security: Requested domain was for this machine.\n")); + return NT_STATUS_LOGON_FAILURE; + } + + /* + * 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)) + { + 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; + } + } + + /* + * Treat each name in the 'password server =' line as a potential + * PDC/BDC. Contact each in turn and try and authenticate. + */ + + password_server = lp_passwordserver(); + + nt_status = domain_client_validate(mem_ctx, user_info, domain, + (uchar *)auth_context->challenge.data, + server_info, + password_server, lp_netbios_name(), SEC_CHAN_WKSTA, trust_passwd, last_change_time); + return nt_status; +} + +/* module initialisation */ +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; + + 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. + * 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_trustdomain_security: Requested domain was for this machine.\n")); + return NT_STATUS_LOGON_FAILURE; + } + + /* + * Check that the requested domain is not our own domain, + * If it is, we should use our own local password file. + */ + + if(strequal(lp_workgroup(), (user_info->domain.str))) { + DEBUG(3,("check_trustdomain_security: Requested domain was for this domain.\n")); + return NT_STATUS_LOGON_FAILURE; + } + + /* + * 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 + + nt_status = domain_client_validate(mem_ctx, user_info, user_info->domain.str, + (uchar *)auth_context->challenge.data, + server_info, "*" /* Do a lookup */, + lp_workgroup(), SEC_CHAN_DOMAIN, trust_md4_password, last_change_time); + + return nt_status; +} + +/* module initialisation */ +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; +} diff --git a/source4/auth/auth_ntlmssp.c b/source4/auth/auth_ntlmssp.c new file mode 100644 index 0000000000..b3dff8dbe6 --- /dev/null +++ b/source4/auth/auth_ntlmssp.c @@ -0,0 +1,138 @@ +/* + 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 const uint8 *auth_ntlmssp_get_challenge(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); +} + +static NTSTATUS auth_ntlmssp_check_password(struct ntlmssp_state *ntlmssp_state) +{ + 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 */ + + sub_set_remote_machine(auth_ntlmssp_state->ntlmssp_state->workstation); + + /* setup the string used by %U */ + /* sub_set_smb_name checks for weird internally */ + sub_set_user_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); + + 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->check_password = auth_ntlmssp_check_password; + (*auth_ntlmssp_state)->ntlmssp_state->server_role = lp_server_role(); + + return NT_STATUS_OK; +} + +NTSTATUS 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_server_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; + return NT_STATUS_OK; +} + +NTSTATUS auth_ntlmssp_update(AUTH_NTLMSSP_STATE *auth_ntlmssp_state, + const DATA_BLOB request, DATA_BLOB *reply) +{ + return ntlmssp_server_update(auth_ntlmssp_state->ntlmssp_state, request, reply); +} + diff --git a/source4/auth/auth_sam.c b/source4/auth/auth_sam.c new file mode 100644 index 0000000000..1d097c94ef --- /dev/null +++ b/source4/auth/auth_sam.c @@ -0,0 +1,564 @@ +/* + 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 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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(DATA_BLOB nt_response, + const uchar *part_passwd, + DATA_BLOB sec_blob, + uint8 user_sess_key[16]) +{ + /* 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 (%d)\n", sec_blob.length)); + return False; + } + + if (nt_response.length != 24) { + DEBUG(0, ("smb_pwd_check_ntlmv1: incorrect password length (%d)\n", nt_response.length)); + return False; + } + + SMBOWFencrypt(part_passwd, sec_blob.data, p24); + if (user_sess_key != NULL) + { + SMBsesskeygen_ntv1(part_passwd, NULL, user_sess_key); + } + + + +#if DEBUG_PASSWORD + DEBUG(100,("Part password (P16) was |")); + dump_data(100, part_passwd, 16); + DEBUG(100,("Password from client was |")); + dump_data(100, nt_response.data, nt_response.length); + DEBUG(100,("Given challenge was |")); + dump_data(100, sec_blob.data, sec_blob.length); + DEBUG(100,("Value from encryption was |")); + 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, + uint8 user_sess_key[16]) +{ + /* 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 (ntv2_response.length < 16) { + /* We MUST have more than 16 bytes, or the stuff below will go + crazy... */ + DEBUG(0, ("smb_pwd_check_ntlmv2: incorrect password length (%d)\n", + 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) + { + SMBsesskeygen_ntv2(kr, value_from_encryption, user_sess_key); + } + +#if DEBUG_PASSWORD + DEBUG(100,("Part password (P16) was |")); + dump_data(100, part_passwd, 16); + DEBUG(100,("Password from client was |")); + dump_data(100, ntv2_response.data, ntv2_response.length); + DEBUG(100,("Variable data from client was |")); + dump_data(100, client_key_data.data, client_key_data.length); + DEBUG(100,("Given challenge was |")); + dump_data(100, sec_blob.data, sec_blob.length); + DEBUG(100,("Value from encryption was |")); + dump_data(100, value_from_encryption, 16); +#endif + data_blob_clear_free(&client_key_data); + return (memcmp(value_from_encryption, client_response, 16) == 0); +} + + +/**************************************************************************** + 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, + uint8 user_sess_key[16]) +{ + uint16 acct_ctrl; + const uint8 *nt_pw, *lm_pw; + uint32 auth_flags; + + 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", pdb_get_username(sampass))); + return(NT_STATUS_OK); + } + else + { + DEBUG(3,("Account for user '%s' has no password and null passwords are NOT allowed.\n", pdb_get_username(sampass))); + return(NT_STATUS_LOGON_FAILURE); + } + } + + auth_flags = user_info->auth_flags; + + if (IS_SAM_DEFAULT(sampass, PDB_NTPASSWD)) { + DEBUG(3,("sam_password_ok: NO NT password stored for user %s.\n", + pdb_get_username(sampass))); + /* No return, we want to check the LM hash below in this case */ + auth_flags &= (~(AUTH_FLAG_NTLMv2_RESP | AUTH_FLAG_NTLM_RESP)); + } + + if (auth_flags & AUTH_FLAG_NTLMv2_RESP) { + nt_pw = pdb_get_nt_passwd(sampass); + /* We have the NT MD4 hash challenge available - see if we can + use it (ie. does it exist in the smbpasswd file). + */ + DEBUG(4,("sam_password_ok: Checking NTLMv2 password with domain [%s]\n", user_info->client_domain.str)); + if (smb_pwd_check_ntlmv2( user_info->nt_resp, + nt_pw, auth_context->challenge, + user_info->smb_name.str, + user_info->client_domain.str, + user_sess_key)) + { + return NT_STATUS_OK; + } + + DEBUG(4,("sam_password_ok: Checking NTLMv2 password without a domain\n")); + if (smb_pwd_check_ntlmv2( user_info->nt_resp, + nt_pw, auth_context->challenge, + user_info->smb_name.str, + "", + user_sess_key)) + { + return NT_STATUS_OK; + } else { + DEBUG(3,("sam_password_ok: NTLMv2 password check failed\n")); + return NT_STATUS_WRONG_PASSWORD; + } + } else if (auth_flags & AUTH_FLAG_NTLM_RESP) { + if (lp_ntlm_auth()) { + nt_pw = pdb_get_nt_passwd(sampass); + /* We have the NT MD4 hash challenge available - see if we can + use it (ie. does it exist in the smbpasswd file). + */ + DEBUG(4,("sam_password_ok: Checking NT MD4 password\n")); + if (smb_pwd_check_ntlmv1(user_info->nt_resp, + nt_pw, auth_context->challenge, + user_sess_key)) + { + return NT_STATUS_OK; + } else { + DEBUG(3,("sam_password_ok: NT MD4 password check failed for user %s\n",pdb_get_username(sampass))); + return NT_STATUS_WRONG_PASSWORD; + } + } else { + DEBUG(2,("sam_password_ok: NTLMv1 passwords NOT PERMITTED for user %s\n",pdb_get_username(sampass))); + /* no return, becouse we might pick up LMv2 in the LM feild */ + } + } + + if (auth_flags & AUTH_FLAG_LM_RESP) { + if (user_info->lm_resp.length != 24) { + DEBUG(2,("sam_password_ok: invalid LanMan password length (%d) for user %s\n", + user_info->nt_resp.length, pdb_get_username(sampass))); + } + + if (!lp_lanman_auth()) { + DEBUG(3,("sam_password_ok: Lanman passwords NOT PERMITTED for user %s\n",pdb_get_username(sampass))); + } else if (IS_SAM_DEFAULT(sampass, PDB_LMPASSWD)) { + DEBUG(3,("sam_password_ok: NO LanMan password set for user %s (and no NT password supplied)\n",pdb_get_username(sampass))); + } else { + lm_pw = pdb_get_lanman_passwd(sampass); + + DEBUG(4,("sam_password_ok: Checking LM password\n")); + if (smb_pwd_check_ntlmv1(user_info->lm_resp, + lm_pw, auth_context->challenge, + user_sess_key)) + { + return NT_STATUS_OK; + } + } + + if (IS_SAM_DEFAULT(sampass, PDB_NTPASSWD)) { + DEBUG(4,("sam_password_ok: LM password check failed for user, no NT password %s\n",pdb_get_username(sampass))); + return NT_STATUS_WRONG_PASSWORD; + } + + nt_pw = pdb_get_nt_passwd(sampass); + + /* This is for 'LMv2' authentication. almost NTLMv2 but limited to 24 bytes. + - related to Win9X, legacy NAS pass-though authentication + */ + DEBUG(4,("sam_password_ok: Checking LMv2 password with domain %s\n", user_info->client_domain.str)); + if (smb_pwd_check_ntlmv2( user_info->lm_resp, + nt_pw, auth_context->challenge, + user_info->smb_name.str, + user_info->client_domain.str, + user_sess_key)) + { + return NT_STATUS_OK; + } + + DEBUG(4,("sam_password_ok: Checking LMv2 password without a domain\n")); + if (smb_pwd_check_ntlmv2( user_info->lm_resp, + nt_pw, auth_context->challenge, + user_info->smb_name.str, + "", + user_sess_key)) + { + 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,("sam_password_ok: Checking NT MD4 password in LM field\n")); + if (lp_ntlm_auth()) + { + if (smb_pwd_check_ntlmv1(user_info->lm_resp, + nt_pw, auth_context->challenge, + user_sess_key)) + { + return NT_STATUS_OK; + } + DEBUG(3,("sam_password_ok: LM password, NT MD4 password in LM field and LMv2 failed for user %s\n",pdb_get_username(sampass))); + return NT_STATUS_WRONG_PASSWORD; + } else { + DEBUG(3,("sam_password_ok: LM password and LMv2 failed for user %s, and NT MD4 password in LM field not permitted\n",pdb_get_username(sampass))); + return NT_STATUS_WRONG_PASSWORD; + } + + } + + /* Should not be reached, but if they send nothing... */ + DEBUG(3,("sam_password_ok: NEITHER LanMan nor NT password supplied for user %s\n",pdb_get_username(sampass))); + return NT_STATUS_WRONG_PASSWORD; +} + +/**************************************************************************** + 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,("Account for user '%s' was disabled.\n", pdb_get_username(sampass))); + return NT_STATUS_ACCOUNT_DISABLED; + } + + /* Test account expire time */ + + kickoff_time = pdb_get_kickoff_time(sampass); + if (kickoff_time != 0 && time(NULL) > kickoff_time) { + DEBUG(1,("Account for user '%s' has expried.\n", pdb_get_username(sampass))); + DEBUG(3,("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,("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,("Account for user '%s' password expired!.\n", pdb_get_username(sampass))); + DEBUG(1,("Password expired at '%s' (%ld) unix time.\n", http_timestring(mem_ctx, 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,("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; + uint8 user_sess_key[16]; + const uint8* lm_hash; + + if (!user_info || !auth_context) { + return NT_STATUS_UNSUCCESSFUL; + } + + /* Can't use the talloc version here, becouse 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,("Couldn't find user '%s' in passdb file.\n", user_info->internal_username.str)); + pdb_free_sam(&sampass); + return NT_STATUS_NO_SUCH_USER; + } + + nt_status = sam_account_ok(mem_ctx, sampass, user_info); + + if (!NT_STATUS_IS_OK(nt_status)) { + pdb_free_sam(&sampass); + return nt_status; + } + + nt_status = sam_password_ok(auth_context, mem_ctx, sampass, user_info, user_sess_key); + + 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; + } + + lm_hash = pdb_get_lanman_passwd((*server_info)->sam_account); + if (lm_hash) { + memcpy((*server_info)->first_8_lm_hash, lm_hash, 8); + } + + memcpy((*server_info)->session_key, user_sess_key, sizeof(user_sess_key)); + + return nt_status; +} + +/* module initialisation */ +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_sam_security; + (*auth_method)->name = "sam"; + 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) +{ + + if (!user_info || !auth_context) { + return NT_STATUS_LOGON_FAILURE; + } + + /* If we are a domain member, we must not + attempt to check the password locally, + unless it is one of our aliases. */ + + if (!is_myname(user_info->domain.str)) { + DEBUG(7,("The requested user domain is not the local server name. [%s]\\[%s]\n", + user_info->domain.str,user_info->internal_username.str)); + return NT_STATUS_NO_SUCH_USER; + } + + return check_sam_security(auth_context, my_private_data, mem_ctx, user_info, server_info); +} + +/* module initialisation */ +NTSTATUS auth_init_samstrict(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 = "samstrict"; + return NT_STATUS_OK; +} + +/**************************************************************************** +Check SAM security (above) but with a few extra checks if we're a DC. +****************************************************************************/ + +static NTSTATUS check_samstrict_dc_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) +{ + + if (!user_info || !auth_context) { + return NT_STATUS_LOGON_FAILURE; + } + + /* If we are a domain member, we must not + attempt to check the password locally, + unless it is one of our aliases, empty + or our domain if we are a logon server.*/ + + + if ((strcasecmp(lp_workgroup(), user_info->domain.str) != 0) && + (!is_myname(user_info->domain.str))) { + DEBUG(7,("The requested user domain is not the local server name or our domain. [%s]\\[%s]\n", + user_info->domain.str,user_info->internal_username.str)); + return NT_STATUS_NO_SUCH_USER; + } + + return check_sam_security(auth_context, my_private_data, mem_ctx, user_info, server_info); +} + +/* module initialisation */ +NTSTATUS auth_init_samstrict_dc(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_dc_security; + (*auth_method)->name = "samstrict_dc"; + return NT_STATUS_OK; +} diff --git a/source4/auth/auth_server.c b/source4/auth/auth_server.c new file mode 100644 index 0000000000..620d9a33c8 --- /dev/null +++ b/source4/auth/auth_server.c @@ -0,0 +1,402 @@ +/* + 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(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, lp_netbios_name(), + desthost, &dest_ip)) { + release_server_mutex(); + DEBUG(1,("password server fails session request\n")); + cli_shutdown(cli); + return NULL; + } + + if (strequal(desthost,myhostname(mem_ctx))) { + 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) { + cli_shutdown(*cli); + } +} + +/**************************************************************************** + Send a 'keepalive' packet down the cli pipe. +****************************************************************************/ + +static void send_server_keepalive(void **private_data_pointer) +{ + struct cli_state **cli = (struct cli_state **)private_data_pointer; + + /* also send a keepalive to the password server if its still + connected */ + if (cli && *cli && (*cli)->initialised) { + if (!send_nbt_keepalive((*cli)->fd)) { + DEBUG( 2, ( "password server keepalive failed.\n")); + cli_shutdown(*cli); + } + } +} + +/**************************************************************************** + 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_LOGON_FAILURE; + 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_LOGON_FAILURE; + } + + 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, lp_netbios_name()); + } + + /* + * 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) { + struct passwd *pass = Get_Pwnam(user_info->internal_username.str); + if (pass) { + nt_status = make_server_info_pw(server_info, pass); + } else { + nt_status = NT_STATUS_NO_SUCH_USER; + } + } + + if (locally_made_cli) { + cli_shutdown(cli); + } + + return(nt_status); +} + +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; +} diff --git a/source4/auth/auth_unix.c b/source4/auth/auth_unix.c new file mode 100644 index 0000000000..4f44767a81 --- /dev/null +++ b/source4/auth/auth_unix.c @@ -0,0 +1,132 @@ +/* + 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); + } else { + /* we need to do somthing more useful here */ + nt_status = NT_STATUS_NO_SUCH_USER; + } + } + + return nt_status; +} + +/* module initialisation */ +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; +} + diff --git a/source4/auth/auth_util.c b/source4/auth/auth_util.c new file mode 100644 index 0000000000..7096361913 --- /dev/null +++ b/source4/auth/auth_util.c @@ -0,0 +1,1222 @@ +/* + 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 *unix_user, 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_user, 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. +****************************************************************************/ + +void smb_user_control(const auth_usersupplied_info *user_info, auth_serversupplied_info *server_info, NTSTATUS nt_status) +{ + struct passwd *pwd=NULL; + + if (NT_STATUS_IS_OK(nt_status)) { + + if (!(server_info->sam_fill_level & SAM_FILL_UNIX)) { + + /* + * 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() && !(pwd = Get_Pwnam(user_info->internal_username.str))) { + smb_create_user(user_info->internal_username.str, NULL); + } + } + } +} + +/**************************************************************************** + 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 %d)\n", 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); + + DEBUG(5, ("make_user_info_map: Mapping user [%s]\\[%s] from workstation [%s]\n", + client_domain, smb_name, wksta_name)); + + if (lp_allow_trusted_domains() && *client_domain) { + + /* the client could have given us a workstation name + or other crap for the workgroup - we really need a + way of telling if this domain name is one of our + trusted domain names + + Also don't allow "" as a domain, fixes a Win9X bug + where it doens't supply a domain for logon script + 'net use' commands. + + The way I do it here is by checking if the fully + qualified username exists. This is rather reliant + on winbind, but until we have a better method this + will have to do + */ + + domain = client_domain; + + if ((smb_name) && (*smb_name)) { /* Don't do this for guests */ + char *user = NULL; + if (asprintf(&user, "%s%s%s", + client_domain, lp_winbind_separator(), + smb_name) < 0) { + DEBUG(0, ("make_user_info_map: asprintf() failed!\n")); + return NT_STATUS_NO_MEMORY; + } + + DEBUG(5, ("make_user_info_map: testing for user %s\n", user)); + + if (Get_Pwnam(user) == NULL) { + DEBUG(5, ("make_user_info_map: test for user %s failed\n", user)); + domain = lp_workgroup(); + DEBUG(5, ("make_user_info_map: trusted domain %s doesn't appear to exist, using %s\n", + client_domain, domain)); + } else { + DEBUG(5, ("make_user_info_map: using trusted domain %s\n", domain)); + } + SAFE_FREE(user); + } + } else { + domain = lp_workgroup(); + } + + 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 uchar *)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, + sub_get_remote_machine(), + 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, + sub_get_remote_machine(), + 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 %i SIDs\n", token->num_sids)); + for (i = 0; i < token->num_sids; i++) + DEBUGADDC(dbg_class, dbg_lev, ("SID[%3i]: %s\n", 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 (!uid_to_sid(&user_sid, uid)) { + return NULL; + } + if (!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 (!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. + ******************************************************************************/ + +static NTSTATUS get_user_groups_from_local_sam(SAM_ACCOUNT *sampass, + int *n_groups, DOM_SID **groups, gid_t **unix_groups) +{ + uid_t uid; + gid_t gid; + int n_unix_groups; + int i; + struct passwd *usr; + + *n_groups = 0; + *groups = NULL; + + if (!IS_SAM_UNIX_USER(sampass)) { + DEBUG(1, ("user %s does not have a unix identity!\n", pdb_get_username(sampass))); + return NT_STATUS_NO_SUCH_USER; + } + + uid = pdb_get_uid(sampass); + gid = pdb_get_gid(sampass); + + n_unix_groups = groups_max(); + if ((*unix_groups = malloc( sizeof(gid_t) * n_unix_groups ) ) == NULL) { + DEBUG(0, ("get_user_groups_from_local_sam: Out of memory allocating unix group list\n")); + passwd_free(&usr); + return NT_STATUS_NO_MEMORY; + } + + if (sys_getgrouplist(pdb_get_username(sampass), 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); + passwd_free(&usr); + return NT_STATUS_NO_MEMORY; + } + *unix_groups = groups_tmp; + + if (sys_getgrouplist(pdb_get_username(sampass), gid, *unix_groups, &n_unix_groups) == -1) { + DEBUG(0, ("get_user_groups_from_local_sam: failed to get the unix group list\n")); + SAFE_FREE(*unix_groups); + passwd_free(&usr); + 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); + + if (n_unix_groups > 0) { + *groups = malloc(sizeof(DOM_SID) * n_unix_groups); + if (!*groups) { + DEBUG(0, ("get_user_group_from_local_sam: 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 (!gid_to_sid(&(*groups)[i], (*unix_groups)[i])) { + DEBUG(1, ("get_user_groups_from_local_sam: 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, SAM_ACCOUNT *sampass) +{ + *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); + + (*server_info)->sam_fill_level = SAM_FILL_ALL; + (*server_info)->sam_account = sampass; + + 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 = NT_STATUS_OK; + 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; + + if (!NT_STATUS_IS_OK(nt_status = make_server_info(server_info, sampass))) { + return nt_status; + } + + if (!NT_STATUS_IS_OK(nt_status + = get_user_groups_from_local_sam(sampass, + &n_groupSIDs, &groupSIDs, &unix_groups))) + { + 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; + + DEBUG(5,("make_server_info_sam: made server info for user %s\n", + pdb_get_username((*server_info)->sam_account))); + + 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, const 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; + } + return make_server_info_sam(server_info, sampass); +} + +/*************************************************************************** + 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)) { + (*server_info)->guest = True; + } + + return nt_status; +} + +/*************************************************************************** + 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) +{ + NTSTATUS nt_status = NT_STATUS_OK; + + const char *nt_domain; + const char *nt_username; + + SAM_ACCOUNT *sam_account = NULL; + DOM_SID user_sid; + DOM_SID group_sid; + + struct passwd *passwd; + + 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 */ + domain = domain; + } + + if (winbind_sid_to_uid(&uid, &user_sid) + && winbind_sid_to_gid(&gid, &group_sid) + && ((passwd = getpwuid_alloc(uid)))) { + nt_status = pdb_init_sam_pw(&sam_account, passwd); + passwd_free(&passwd); + } else { + char *dom_user; + dom_user = talloc_asprintf(mem_ctx, "%s%s%s", + nt_domain, + lp_winbind_separator(), + internal_username); + + if (!dom_user) { + DEBUG(0, ("talloc_asprintf failed!\n")); + return NT_STATUS_NO_MEMORY; + } else { + + if (!(passwd = Get_Pwnam(dom_user)) + /* Only lookup local for the local + domain, we don't want this for + trusted domains */ + && strequal(nt_domain, lp_workgroup())) { + passwd = Get_Pwnam(internal_username); + } + + if (!passwd) { + return NT_STATUS_NO_SUCH_USER; + } else { + nt_status = pdb_init_sam_pw(&sam_account, passwd); + } + } + } + + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(0, ("make_server_info_info3: pdb_init_sam failed!\n")); + return nt_status; + } + + 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_nt_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_fullname(sam_account, unistr2_static(mem_ctx, &(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(mem_ctx, &(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(mem_ctx, &(info3->uni_profile_path)), PDB_CHANGED)) { + pdb_free_sam(&sam_account); + return NT_STATUS_NO_MEMORY; + } + + if (!pdb_set_homedir(sam_account, unistr2_static(mem_ctx, &(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(mem_ctx, &(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, sam_account))) { + DEBUG(4, ("make_server_info failed!\n")); + pdb_free_sam(&sam_account); + return nt_status; + } + + /* Store the user group information in the server_info + returned to the caller. */ + + if (!NT_STATUS_IS_OK(nt_status + = get_user_groups_from_local_sam(sam_account, + &n_lgroupSIDs, + &lgroupSIDs, + &unix_groups))) + { + DEBUG(4,("get_user_groups_from_local_sam 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) * + (n_lgroupSIDs + info3->num_groups2 + + info3->num_other_sids)); + if (!all_group_SIDs) { + DEBUG(0, ("create_nt_token_info3: malloc() failed for DOM_SID list!\n")); + SAFE_FREE(lgroupSIDs); + return NT_STATUS_NO_MEMORY; + } + + /* Copy the 'local' sids */ + memcpy(all_group_SIDs, lgroupSIDs, sizeof(DOM_SID) * n_lgroupSIDs); + SAFE_FREE(lgroupSIDs); + + /* and create (by appending rids) the 'domain' sids */ + for (i = 0; i < info3->num_groups2; i++) { + sid_copy(&all_group_SIDs[i+n_lgroupSIDs], &(info3->dom_sid.sid)); + if (!sid_append_rid(&all_group_SIDs[i+n_lgroupSIDs], info3->gids[i].g_rid)) { + nt_status = NT_STATUS_INVALID_PARAMETER; + DEBUG(3,("create_nt_token_info3: could not append additional group rid 0x%x\n", + info3->gids[i].g_rid)); + SAFE_FREE(lgroupSIDs); + 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[ + n_lgroupSIDs + 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? */ + if (!NT_STATUS_IS_OK( + nt_status = create_nt_user_token( + &user_sid, &group_sid, + n_lgroupSIDs + info3->num_groups2 + info3->num_other_sids, + all_group_SIDs, False, &token))) { + DEBUG(4,("create_nt_user_token failed\n")); + SAFE_FREE(all_group_SIDs); + return nt_status; + } + + (*server_info)->ptok = token; + + SAFE_FREE(all_group_SIDs); + + 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); + 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; + } +} + + + diff --git a/source4/auth/auth_winbind.c b/source4/auth/auth_winbind.c new file mode 100644 index 0000000000..5e1567d3c1 --- /dev/null +++ b/source4/auth/auth_winbind.c @@ -0,0 +1,136 @@ +/* + 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(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, 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_UNSUCCESSFUL; + } + + /* Send off request */ + + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + request.data.auth_crap.flags = WINBIND_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); + + result = winbindd_request(WINBINDD_PAM_AUTH_CRAP, &request, &response); + + 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_UNSUCCESSFUL; + } + + return nt_status; +} + +/* module initialisation */ +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; + return NT_STATUS_OK; +} diff --git a/source4/auth/pampass.c b/source4/auth/pampass.c new file mode 100644 index 0000000000..045ceb7c72 --- /dev/null +++ b/source4/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 + +/* + * 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(t->prompt); + trim_string(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(t->reply); + trim_string(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_string(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_string(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/source4/auth/pass_check.c b/source4/auth/pass_check.c new file mode 100644 index 0000000000..88b82e3474 --- /dev/null +++ b/source4/auth/pass_check.c @@ -0,0 +1,784 @@ +/* + 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 +#include + +/******************************************************************* +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 +#include + +/***************************************************************** + This new version of the DFS_AUTH code was donated by Karsten Muuss + . 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(¤t_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(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(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/source4/autogen.sh b/source4/autogen.sh new file mode 100755 index 0000000000..a2228a6fd9 --- /dev/null +++ b/source4/autogen.sh @@ -0,0 +1,36 @@ +#! /bin/sh + +# Run this script to build samba from CVS. + +## first try the default names +AUTOHEADER="autoheader" +AUTOCONF="autoconf" + +if which $AUTOCONF > /dev/null +then + : +else + echo "$0: need autoconf 2.53 or later to build samba from CVS" >&2 + exit 1 +fi + +## +## what version do we need? +## +if [ `$AUTOCONF --version | head -1 | cut -d. -f 2` -lt 53 ]; then + + ## maybe it's installed under a different name (e.g. RedHat 7.3) + + AUTOCONF="autoconf-2.53" + AUTOHEADER="autoheader-2.53" + +fi + +echo "$0: running $AUTOHEADER" +$AUTOHEADER || exit 1 + +echo "$0: running $AUTOCONF" +$AUTOCONF || exit 1 + +echo "Now run ./configure and then make." +exit 0 diff --git a/source4/build/tests/README b/source4/build/tests/README new file mode 100644 index 0000000000..cf1be8b00a --- /dev/null +++ b/source4/build/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/source4/build/tests/crypttest.c b/source4/build/tests/crypttest.c new file mode 100644 index 0000000000..efee2e593d --- /dev/null +++ b/source4/build/tests/crypttest.c @@ -0,0 +1,852 @@ +#if defined(HAVE_UNISTD_H) +#include +#endif + +#include + +#ifdef HAVE_STRING_H +#include +#endif + +#ifdef HAVE_STRINGS_H +#include +#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 + 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/source4/build/tests/fcntl_lock.c b/source4/build/tests/fcntl_lock.c new file mode 100644 index 0000000000..3dc12a3897 --- /dev/null +++ b/source4/build/tests/fcntl_lock.c @@ -0,0 +1,121 @@ +/* test whether fcntl locking works on this system */ + +#if defined(HAVE_UNISTD_H) +#include +#endif + +#include +#include +#include + +#ifdef HAVE_FCNTL_H +#include +#endif + +#ifdef HAVE_SYS_FCNTL_H +#include +#endif + +#ifdef HAVE_SYS_WAIT_H +#include +#endif + +#include + +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/source4/build/tests/fcntl_lock64.c b/source4/build/tests/fcntl_lock64.c new file mode 100644 index 0000000000..e5ecd88fd0 --- /dev/null +++ b/source4/build/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 +#endif + +#include +#include +#include + +#ifdef HAVE_FCNTL_H +#include +#endif + +#ifdef HAVE_SYS_FCNTL_H +#include +#endif + +#include + +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/source4/build/tests/fcntl_lock_thread.c b/source4/build/tests/fcntl_lock_thread.c new file mode 100644 index 0000000000..f31105626c --- /dev/null +++ b/source4/build/tests/fcntl_lock_thread.c @@ -0,0 +1,122 @@ +/* test whether fcntl locking works between threads on this Linux system */ + +#include + +#include +#include +#include + +#include + +#include + +#include + +#include +#include + +static int sys_waitpid(pid_t pid,int *status,int options) +{ + return waitpid(pid,status,options); +} + +#define DATA "conftest.fcntl" + +#define SEEK_SET 0 + +static void *test_thread(void *thread_parm) +{ + int *status = thread_parm; + int fd, ret; + struct flock lock; + + sleep(2); + fd = open(DATA, O_RDWR); + + if (fd == -1) { + fprintf(stderr,"ERROR: failed to open %s (errno=%d)\n", + DATA, (int)errno); + pthread_exit(thread_parm); + } + + lock.l_type = F_WRLCK; + lock.l_whence = SEEK_SET; + lock.l_start = 0; + lock.l_len = 4; + lock.l_pid = 0; + + /* check if a lock applies */ + ret = fcntl(fd,F_SETLK,&lock); + if ((ret != -1)) { + fprintf(stderr,"ERROR: lock test failed (ret=%d errno=%d)\n", ret, (int)errno); + } else { + *status = 0; /* SUCCESS! */ + } + pthread_exit(thread_parm); +} + +/* lock a byte range in a open file */ +int main(int argc, char *argv[]) +{ + struct flock lock; + int fd, ret, status=1, rc; + pid_t pid; + char *testdir = NULL; + pthread_t thread_id; + pthread_attr_t thread_attr; + + testdir = getenv("TESTDIR"); + if (testdir) chdir(testdir); + + alarm(10); + + pthread_attr_init(&thread_attr); + pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED); + rc = pthread_create(&thread_id, &thread_attr, &test_thread, &status); + pthread_attr_destroy(&thread_attr); + if (rc == 0) { + fprintf(stderr,"created thread_id=%lu\n", + (unsigned long int)thread_id); + } else { + fprintf(stderr,"ERROR: thread create failed, rc=%d\n", rc); + } + + unlink(DATA); + fd = open(DATA, O_RDWR|O_CREAT|O_RDWR, 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); + + sleep(4); /* allow thread to try getting lock */ + + 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/source4/build/tests/ftruncate.c b/source4/build/tests/ftruncate.c new file mode 100644 index 0000000000..93282782ee --- /dev/null +++ b/source4/build/tests/ftruncate.c @@ -0,0 +1,27 @@ +/* test whether ftruncte() can extend a file */ + +#if defined(HAVE_UNISTD_H) +#include +#endif + +#include +#include +#include + +#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/source4/build/tests/getgroups.c b/source4/build/tests/getgroups.c new file mode 100644 index 0000000000..343fd5a184 --- /dev/null +++ b/source4/build/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 +#endif + +#include +#include +#include +#include + +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 +#endif +#include +#include +#include +#include + +#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/source4/build/tests/shlib.c b/source4/build/tests/shlib.c new file mode 100644 index 0000000000..761d9fd5c5 --- /dev/null +++ b/source4/build/tests/shlib.c @@ -0,0 +1,6 @@ +/* a trivial function used to test building shared libraries */ + +int foo(void) +{ + return 1; +} diff --git a/source4/build/tests/summary.c b/source4/build/tests/summary.c new file mode 100644 index 0000000000..d3708c236c --- /dev/null +++ b/source4/build/tests/summary.c @@ -0,0 +1,30 @@ +#include + +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"); + /* REWRITE: 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/source4/build/tests/trivial.c b/source4/build/tests/trivial.c new file mode 100644 index 0000000000..2723637a0f --- /dev/null +++ b/source4/build/tests/trivial.c @@ -0,0 +1,4 @@ +main() +{ + exit(0); +} diff --git a/source4/build/tests/unixsock.c b/source4/build/tests/unixsock.c new file mode 100644 index 0000000000..f2765d68f6 --- /dev/null +++ b/source4/build/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 , June 2000. */ + +/* TODO: Look for AF_LOCAL (most standard), AF_UNIX, and AF_FILE. */ + +#include + +#ifdef HAVE_SYS_SOCKET_H +# include +#endif + +#ifdef HAVE_SYS_UN_H +# include +#endif + +#ifdef HAVE_SYS_TYPES_H +# include +#endif + +#if HAVE_SYS_WAIT_H +# include +#endif + +#if HAVE_ERRNO_DECL +# include +#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/source4/change-log b/source4/change-log new file mode 100644 index 0000000000..71f5012484 --- /dev/null +++ b/source4/change-log @@ -0,0 +1,1878 @@ +SUPERCEDED Change Log for Samba +^^^^^^^^^^ + +Unless otherwise attributed, all changes were made by +Andrew.Tridgell@anu.edu.au. + +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{ ) + - 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{ + - 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 ) + - 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 + - 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 + - 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 + - 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 + - enabled printing with a SMBclose and SMBwrite for NT + thanks to jkf@frisky.Franz.COM (Sean Foderaro) + - SGI changes from Michael Chua + - 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 ) + - 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 + - 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 + - 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 + - ifdef'd EDQUOT in case you don't have it (thanks to Paul Blackman ) + - 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 + - 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 + + - 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 + - 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 + - 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 ) + - 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 + - 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 + - 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 + - password changing code from Bob Nance + 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 + - 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 + - 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 + - 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 + ) + - 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 + - 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 and + Charles Gay-Jones + - 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 + - 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 + - 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 + - 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 + - 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 + - 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 + ) + - 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 + - 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 + + - check with FQDN in access.c (thanks to Arne Ansper ) + - default broadcast for dnix from Peter Olsson + - solaris patches from Ronald Guilmette + - 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 ) + - 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 + - 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 + - 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 + + - released alpha8 + - fixed rename directory bug + - nmbd wins fix from Maximilian Errath + - 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 + - released alpha12 + - pwlen fix in NETGROUP from Andrew J Cole + - 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 + + - 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 + + - 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 + - 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 ) + - 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 + . 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 + - released p4 + - smbpasswd fix from Jeremy + - compilation fixes from Magnus Hyllander + - added NetServerEnum to ipc.c (needed for master browser stuff) + - Makefile fix from Gunther Mayer + - 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 + + - 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 + - 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 + - strip dot fix from Arne Ansper + - added addtosmbpass + man page from + michal@ellpspace.math.ualberta.ca (Michal Jaegermann) + - pcap fix for AIX from Jon Christiansen + - 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 + + - made is_8_3() handle full paths + - rewrote open_file_shared() with help from Charles Hoch + + - 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" + + - 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 + - fixed become_user() bug that led to incorrect permissions in + some situations. + - released p6 + - is_8_3() fix from Charles Hoch + - nmblib bugfix from gmk@mhcnet.att.com (George Kull) + - aix pcap fix from Jon Christiansen + - 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 + ) + - 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 + - added domain logon and logon script suport + - SCO openserver 5 patches from Scott Michel + - Makefile changes from Marty Leisner + - chgpasswd changes from Roman Dumych + for SVR4 + - GUEST_SESSSETUP change from David.Chappell@mail.cc.trincoll.edu + - released p7 + - moved SO_REUSEADDR before bind() (thanks to Thomas Bellman + ) + - 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 + - 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 + - svr4 pause/resume printing patch from Brendan O'Dea (bod@tyndall.com.au) + - fixed master announcement thanks to Luke Leighton + - 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 + + - SMBtranss2 fix for OS/2 from Jeremy Allison + - profiles fixup from Timm Wetzel + - man page updates from Dirk.DeWachter@rug.ac.be + - nmbsync fix from Andy Whitcroft + - Lynx patches from Manfred Woelfel + - 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/source4/client/.cvsignore b/source4/client/.cvsignore new file mode 100644 index 0000000000..49a52f7616 --- /dev/null +++ b/source4/client/.cvsignore @@ -0,0 +1 @@ +client_proto.h \ No newline at end of file diff --git a/source4/client/client.c b/source4/client/client.c new file mode 100644 index 0000000000..5c72da1731 --- /dev/null +++ b/source4/client/client.c @@ -0,0 +1,3025 @@ +/* + Unix SMB/CIFS implementation. + SMB client + Copyright (C) Andrew Tridgell 1994-1998 + Copyright (C) Simo Sorce 2001-2002 + Copyright (C) Jelmer Vernooij 2003 + Copyright (C) James J Myers 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 "../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 char *cmdstr = NULL; + +static int io_bufsize = 64512; + +static int name_type = 0x20; + +static int process_tok(fstring 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); + + +/******************************************************************* + 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(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); +} + +/**************************************************************************** +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 < CLI_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= 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) +{ + fstring 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 & FILE_ATTRIBUTE_DIRECTORY) return(True); + + if (*fileselection && + !mask_match(cli, 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 & FILE_ATTRIBUTE_ARCHIVE)) { + DEBUG(3,("archive %s failed\n", finfo->name)); + return(False); + } + + return(True); +} + +/******************************************************************* + Return a string representing an attribute for a file. +********************************************************************/ +static const char *attrib_string(uint16 mode) +{ + static fstring attrstr; + int i, len; + const struct { + char c; + uint16 attr; + } attr_strs[] = { + {'V', FILE_ATTRIBUTE_VOLUME}, + {'D', FILE_ATTRIBUTE_DIRECTORY}, + {'A', FILE_ATTRIBUTE_ARCHIVE}, + {'H', FILE_ATTRIBUTE_HIDDEN}, + {'S', FILE_ATTRIBUTE_SYSTEM}, + {'R', FILE_ATTRIBUTE_READONLY}, + {'d', FILE_ATTRIBUTE_DEVICE}, + {'t', FILE_ATTRIBUTE_TEMPORARY}, + {'s', FILE_ATTRIBUTE_SPARSE}, + {'r', FILE_ATTRIBUTE_REPARSE_POINT}, + {'c', FILE_ATTRIBUTE_COMPRESSED}, + {'o', FILE_ATTRIBUTE_OFFLINE}, + {'n', FILE_ATTRIBUTE_NONINDEXED}, + {'e', FILE_ATTRIBUTE_ENCRYPTED} + }; + + for (len=i=0; imtime; /* 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(do_list_queue + do_list_queue_end, entry, + do_list_queue_size - do_list_queue_end - 1); + do_list_queue_end = new_end; + DEBUG(4,("added %s to do_list_queue (start=%d, end=%d)\n", + entry, (int)do_list_queue_start, (int)do_list_queue_end)); + } +} + +static char *do_list_queue_head(void) +{ + return do_list_queue + do_list_queue_start; +} + +static void remove_do_list_queue_head(void) +{ + if (do_list_queue_end > do_list_queue_start) + { + do_list_queue_start += strlen(do_list_queue_head()) + 1; + adjust_do_list_queue(); + DEBUG(4,("removed head of do_list_queue (start=%d, end=%d)\n", + (int)do_list_queue_start, (int)do_list_queue_end)); + } +} + +static int do_list_queue_empty(void) +{ + return (! (do_list_queue && *do_list_queue)); +} + +/**************************************************************************** +a helper for do_list + ****************************************************************************/ +static void do_list_helper(file_info *f, const char *mask, void *state) +{ + if (f->mode & FILE_ATTRIBUTE_DIRECTORY) { + 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; + + 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 = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN; + pstring mask; + fstring 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, 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 = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN; + pstring mask; + fstring 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, const 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(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 & FILE_ATTRIBUTE_ARCHIVE)) { + cli_setatr(cli, rname, attr & ~(uint16)FILE_ATTRIBUTE_ARCHIVE, 0); + } + + { + struct timeval tp_end; + int this_time; + + GetTimeOfDay(&tp_end); + this_time = + (tp_end.tv_sec - tp_start.tv_sec)*1000 + + (tp_end.tv_usec - tp_start.tv_usec)/1000; + get_total_time_ms += this_time; + get_total_size += nread; + + DEBUG(2,("(%3.1f kb/s) (average %3.1f kb/s)\n", + nread / (1.024*this_time + 1.0e-4), + get_total_size / (1.024*get_total_time_ms))); + } + + return rc; +} + + +/**************************************************************************** + get a file + ****************************************************************************/ +static int cmd_get(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 \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 a 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 & FILE_ATTRIBUTE_DIRECTORY) + 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 & FILE_ATTRIBUTE_DIRECTORY)) { + 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(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, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_DIRECTORY,do_mget,False, True); + chdir(".."); + pstrcpy(cur_dir,saved_curdir); +} + + +/**************************************************************************** +view the file using the pager +****************************************************************************/ +static int cmd_more(void) +{ + fstring rname,lname,pager_cmd; + char *pager; + int fd; + int rc = 0; + + fstrcpy(rname,cur_dir); + fstrcat(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 \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 = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN; + pstring mget_mask; + fstring buf; + char *p=buf; + + *mget_mask = 0; + + if (recurse) + attribute |= FILE_ATTRIBUTE_DIRECTORY; + + 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) +{ + const char *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; + fstring buf; + char *p=buf; + + pstrcpy(mask,cur_dir); + + if (!next_token_nr(NULL,p,NULL,sizeof(buf))) { + if (!recurse) + d_printf("mkdir \n"); + return 1; + } + pstrcat(mask,p); + + if (recurse) { + pstring ddir; + pstring ddir2; + *ddir2 = 0; + + pstrcpy(ddir,mask); + trim_string(ddir,".",NULL); + 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; + fstring buf; + char *p=buf; + + pstrcpy(name,cur_dir); + + if (!next_token_nr(NULL,p,NULL,sizeof(buf))) { + d_printf("altname \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; + fstring buf; + char *p=buf; + + pstrcpy(rname,cur_dir); + pstrcat(rname,"\\"); + + if (!next_token_nr(NULL,p,NULL,sizeof(buf))) { + d_printf("put \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) +{ + fstring 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) +{ + d_printf("REWRITE: print job cancel not implemented\n"); + return 1; +} + + +/**************************************************************************** + cancel a print job + ****************************************************************************/ +static int cmd_cancel(void) +{ + fstring buf; + int job; + + if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) { + d_printf("cancel ...\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 \n"); + return 1; + } + + pstrcpy(rname,lname); + p = strrchr_m(rname,'/'); + if (p) { + slprintf(rname, sizeof(rname)-1, "%s-%d", p+1, (int)getpid()); + } + + if (strequal(lname,"-")) { + slprintf(rname, sizeof(rname)-1, "stdin-%d", (int)getpid()); + } + + return do_put(rname, lname, False); +} + + +/**************************************************************************** + show a print queue +****************************************************************************/ +static int cmd_queue(void) +{ + d_printf("REWRITE: print job queue not implemented\n"); + + 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 & FILE_ATTRIBUTE_DIRECTORY) + 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; + fstring buf; + uint16 attribute = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN; + + if (recurse) + attribute |= FILE_ATTRIBUTE_DIRECTORY; + + pstrcpy(mask,cur_dir); + + if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) { + d_printf("del \n"); + return 1; + } + pstrcat(mask,buf); + + do_list(mask, attribute,do_del,False,False); + + return 0; +} + + +/**************************************************************************** +delete a whole directory tree +****************************************************************************/ +static int cmd_deltree(void) +{ + pstring dname; + fstring buf; + int ret; + + pstrcpy(dname,cur_dir); + + if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) { + d_printf("deltree \n"); + return 1; + } + pstrcat(dname,buf); + + ret = cli_deltree(cli, dname); + + if (ret == -1) { + printf("Failed to delete tree %s - %s\n", dname, cli_errstr(cli)); + return -1; + } + + printf("Deleted %d files in %s\n", ret, dname); + + return 0; +} + + +/**************************************************************************** +show as much information as possible about a file +****************************************************************************/ +static int cmd_allinfo(void) +{ + pstring fname; + fstring buf; + int ret = 0; + TALLOC_CTX *mem_ctx; + union smb_fileinfo finfo; + NTSTATUS status; + + pstrcpy(fname,cur_dir); + + if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) { + d_printf("deltree \n"); + return 1; + } + pstrcat(fname,buf); + + mem_ctx = talloc_init(fname); + + /* first a ALL_INFO QPATHINFO */ + finfo.generic.level = RAW_FILEINFO_ALL_INFO; + finfo.generic.in.fname = fname; + status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo); + if (!NT_STATUS_IS_OK(status)) { + d_printf("%s - %s\n", fname, nt_errstr(status)); + ret = 1; + goto done; + } + + d_printf("\tcreate_time: %s\n", nt_time_string(mem_ctx, &finfo.all_info.out.create_time)); + d_printf("\taccess_time: %s\n", nt_time_string(mem_ctx, &finfo.all_info.out.access_time)); + d_printf("\twrite_time: %s\n", nt_time_string(mem_ctx, &finfo.all_info.out.write_time)); + d_printf("\tchange_time: %s\n", nt_time_string(mem_ctx, &finfo.all_info.out.change_time)); + d_printf("\tattrib: 0x%x\n", finfo.all_info.out.attrib); + d_printf("\talloc_size: %lu\n", (unsigned long)finfo.all_info.out.alloc_size); + d_printf("\tsize: %lu\n", (unsigned long)finfo.all_info.out.size); + d_printf("\tnlink: %u\n", finfo.all_info.out.nlink); + d_printf("\tdelete_pending: %u\n", finfo.all_info.out.delete_pending); + d_printf("\tdirectory: %u\n", finfo.all_info.out.directory); + d_printf("\tea_size: %u\n", finfo.all_info.out.ea_size); + d_printf("\tfname: '%s'\n", finfo.all_info.out.fname.s); + + /* 8.3 name if any */ + finfo.generic.level = RAW_FILEINFO_ALT_NAME_INFO; + status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo); + if (NT_STATUS_IS_OK(status)) { + d_printf("\talt_name: %s\n", finfo.alt_name_info.out.fname.s); + } + + /* dev/inode if available */ + finfo.generic.level = RAW_FILEINFO_INTERNAL_INFORMATION; + status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo); + if (NT_STATUS_IS_OK(status)) { + d_printf("\tdevice 0x%x\n", finfo.internal_information.out.device); + d_printf("\tinode 0x%x\n", finfo.internal_information.out.inode); + } + + /* the EAs, if any */ + finfo.generic.level = RAW_FILEINFO_ALL_EAS; + status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo); + if (NT_STATUS_IS_OK(status)) { + int i; + for (i=0;itree, mem_ctx, &finfo); + if (NT_STATUS_IS_OK(status)) { + int i; + for (i=0;itree, mem_ctx, &finfo); + if (NT_STATUS_IS_OK(status)) { + d_printf("\tcompressed size %ld\n", (long)finfo.compression_info.out.compressed_size); + d_printf("\tformat %ld\n", (long)finfo.compression_info.out.format); + d_printf("\tunit_shift %ld\n", (long)finfo.compression_info.out.unit_shift); + d_printf("\tchunk_shift %ld\n", (long)finfo.compression_info.out.chunk_shift); + d_printf("\tcluster_shift %ld\n", (long)finfo.compression_info.out.cluster_shift); + } + + talloc_destroy(mem_ctx); + +done: + return ret; +} + + +/**************************************************************************** +****************************************************************************/ +static int cmd_open(void) +{ + pstring mask; + fstring buf; + + pstrcpy(mask,cur_dir); + + if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) { + d_printf("open \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; + fstring buf; + + pstrcpy(mask,cur_dir); + + if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) { + d_printf("rmdir \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; + fstring buf,buf2; + + if (!(cli->transport->negotiate.capabilities & CAP_UNIX)) { + d_printf("Server doesn't support UNIX CIFS calls.\n"); + return 1; + } + + pstrcpy(src,cur_dir); + pstrcpy(dest,cur_dir); + + if (!next_token(NULL,buf,NULL,sizeof(buf)) || + !next_token(NULL,buf2,NULL, sizeof(buf2))) { + d_printf("link \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; + fstring buf,buf2; + + if (!(cli->transport->negotiate.capabilities & CAP_UNIX)) { + d_printf("Server doesn't support UNIX CIFS calls.\n"); + return 1; + } + + pstrcpy(src,cur_dir); + pstrcpy(dest,cur_dir); + + if (!next_token(NULL,buf,NULL,sizeof(buf)) || + !next_token(NULL,buf2,NULL, sizeof(buf2))) { + d_printf("symlink \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; + fstring buf, buf2; + + if (!(cli->transport->negotiate.capabilities & CAP_UNIX)) { + d_printf("Server doesn't support UNIX CIFS calls.\n"); + return 1; + } + + pstrcpy(src,cur_dir); + + if (!next_token(NULL,buf,NULL,sizeof(buf)) || + !next_token(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; + fstring buf, buf2, buf3; + + if (!(cli->transport->negotiate.capabilities & CAP_UNIX)) { + d_printf("Server doesn't support UNIX CIFS calls.\n"); + return 1; + } + + pstrcpy(src,cur_dir); + + if (!next_token(NULL,buf,NULL,sizeof(buf)) || + !next_token(NULL,buf2,NULL, sizeof(buf2)) || + !next_token(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 files +****************************************************************************/ +static int cmd_rename(void) +{ + pstring src,dest; + fstring 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 \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; +} + + +/**************************************************************************** +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) +{ + fstring 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) +{ + fstring 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) +{ + fstring 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 \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; + fstring 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 \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); +} + + +/**************************************************************************** +try and browse available connections on a host +****************************************************************************/ +static BOOL browse_host(BOOL sort) +{ + d_printf("REWRITE: host browsing not implemented\n"); + + return False; +} + +/**************************************************************************** +try and browse available connections on a host +****************************************************************************/ +static BOOL list_servers(char *wk_grp) +{ + d_printf("REWRITE: list servers not implemented\n"); + return False; +} + +/* Some constants for completing filename arguments */ + +#define COMPL_NONE 0 /* No completions */ +#define COMPL_REMOTE 1 /* Complete remote filename */ +#define COMPL_LOCAL 2 /* Complete local filename */ + +/* 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," show alt name",{COMPL_NONE,COMPL_NONE}}, + {"allinfo",cmd_allinfo," show all possible info about a file",{COMPL_NONE,COMPL_NONE}}, + {"archive",cmd_archive,"\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 (default 20)",{COMPL_NONE,COMPL_NONE}}, + {"cancel",cmd_cancel," 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," chmod a file using UNIX permission",{COMPL_REMOTE,COMPL_REMOTE}}, + {"chown",cmd_chown," chown a file using UNIX uids and gids",{COMPL_REMOTE,COMPL_REMOTE}}, + {"del",cmd_del," delete all matching files",{COMPL_REMOTE,COMPL_NONE}}, + {"deltree",cmd_deltree," delete a whole directory tree",{COMPL_REMOTE,COMPL_NONE}}, + {"dir",cmd_dir," list the contents of the current directory",{COMPL_REMOTE,COMPL_NONE}}, + {"du",cmd_du," 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," [local name] get a file",{COMPL_REMOTE,COMPL_LOCAL}}, + {"help",cmd_help,"[command] give help on a command",{COMPL_NONE,COMPL_NONE}}, + {"history",cmd_history,"displays the command history",{COMPL_NONE,COMPL_NONE}}, + {"lcd",cmd_lcd,"[directory] change/report the local current working directory",{COMPL_LOCAL,COMPL_NONE}}, + {"link",cmd_link," 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," list the contents of the current directory",{COMPL_REMOTE,COMPL_NONE}}, + {"mask",cmd_select," mask all filenames against this",{COMPL_REMOTE,COMPL_NONE}}, + {"md",cmd_mkdir," make a directory",{COMPL_NONE,COMPL_NONE}}, + {"mget",cmd_mget," get all the matching files",{COMPL_REMOTE,COMPL_NONE}}, + {"mkdir",cmd_mkdir," make a directory",{COMPL_NONE,COMPL_NONE}}, + {"more",cmd_more," view a remote file with your pager",{COMPL_REMOTE,COMPL_NONE}}, + {"mput",cmd_mput," put all matching files",{COMPL_REMOTE,COMPL_NONE}}, + {"newer",cmd_newer," only mget files newer than the specified local file",{COMPL_LOCAL,COMPL_NONE}}, + {"open",cmd_open," open a file",{COMPL_REMOTE,COMPL_NONE}}, + {"print",cmd_print," print a file",{COMPL_NONE,COMPL_NONE}}, + {"printmode",cmd_printmode," 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," [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," remove a directory",{COMPL_NONE,COMPL_NONE}}, + {"recurse",cmd_recurse,"toggle directory recursion for mget and mput",{COMPL_NONE,COMPL_NONE}}, + {"reget",cmd_reget," [local name] get a file restarting at end of local file",{COMPL_REMOTE,COMPL_LOCAL}}, + {"rename",cmd_rename," rename some files",{COMPL_REMOTE,COMPL_REMOTE}}, + {"reput",cmd_reput," [remote name] put a file restarting at end of remote file",{COMPL_LOCAL,COMPL_REMOTE}}, + {"rm",cmd_del," delete all matching files",{COMPL_REMOTE,COMPL_NONE}}, + {"rmdir",cmd_rmdir," remove a directory",{COMPL_NONE,COMPL_NONE}}, + {"setmode",cmd_setmode,"filename change modes of file",{COMPL_REMOTE,COMPL_NONE}}, + {"symlink",cmd_symlink," create a UNIX symlink",{COMPL_REMOTE,COMPL_REMOTE}}, + {"tar",cmd_tar,"tar [IXFqbgNan] current directory to/from ",{COMPL_NONE,COMPL_NONE}}, + {"tarmode",cmd_tarmode," tar's behaviour towards archive bits",{COMPL_NONE,COMPL_NONE}}, + {"translate",cmd_translate,"toggle text translation for printing",{COMPL_NONE,COMPL_NONE}}, + + /* Yes, this must be here, see crh's comment above. */ + {"!",NULL,"run a shell command on the local system",{COMPL_NONE,COMPL_NONE}}, + {NULL,NULL,NULL,{COMPL_NONE,COMPL_NONE}} +}; + + +/******************************************************************* + lookup a command string in the list of commands, including + abbreviations + ******************************************************************/ +static int process_tok(fstring 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; + fstring 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; + fstring 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 & FILE_ATTRIBUTE_DIRECTORY)) + 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 & FILE_ATTRIBUTE_DIRECTORY) + pstrcat(tmp, "/"); + info->matches[info->count] = strdup(tmp); + } + if (info->matches[info->count] == NULL) + return; + if (f->mode & FILE_ATTRIBUTE_DIRECTORY) + smb_readline_ca_char(0); + + if (info->count == 1) + info->samelen = strlen(info->matches[info->count]); + else + while (strncmp(info->matches[info->count], info->matches[info->count-1], info->samelen) != 0) + info->samelen--; + info->count++; + } +} + +static char **remote_completion(const char *text, int len) +{ + pstring dirmask; + int i; + completion_remote_t info = { "", NULL, 1, len, text, 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; + snprintf(dirmask, sizeof(dirmask), "%s%*s*", cur_dir, i-1, text); + } else + snprintf(dirmask, sizeof(dirmask), "%s*", cur_dir); + + if (cli_list(cli, dirmask, + FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN, + completion_remote_filter, &info) < 0) + goto cleanup; + + if (info.count == 2) + info.matches[0] = strdup(info.matches[1]); + else { + info.matches[0] = malloc(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: + while (i >= 0) { + free(matches[i]); + 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->transport->socket->fd == -1) + return; + + FD_ZERO(&fds); + FD_SET(cli->transport->socket->fd, &fds); + + timeout.tv_sec = 0; + timeout.tv_usec = 0; + sys_select_intr(cli->transport->socket->fd+1,&fds,NULL,NULL,&timeout); + + /* We deliberately use cli_swallow_keepalives instead of + client_receive_smb as we want to receive + session keepalives and then drop them here. + */ + if (FD_ISSET(cli->transport->socket->fd, &fds)) { + if (!cli_request_receive_next(cli->transport)) { + d_printf("Lost connection to server\n"); + exit(1); + } + goto again; + } + + if (cli->tree) { + cli_chkpath(cli, "\\"); + } +} + + +/**************************************************************************** +process commands on stdin +****************************************************************************/ +static void process_stdin(void) +{ + const char *ptr; + + while (1) { + fstring tok; + fstring 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; + fstring servicename; + char *sharename; + + /* make a copy so we don't modify the global string 'service' */ + fstrcpy(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, lp_netbios_name(), 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_state_init()) || !cli_socket_connect(c, server_n, &ip)) { + d_printf("Connection to %s failed\n", server_n); + return NULL; + } + + if (!cli_transport_establish(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) { + const char *pass = getpass("Password: "); + if (pass) { + pstrcpy(password, pass); + } + } + + if (!cli_session_setup(c, username, 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, "", "", lp_workgroup())) { + d_printf("session setup failed: %s\n", cli_errstr(c)); + cli_shutdown(c); + return NULL; + } + d_printf("Anonymous login successful\n"); + } + + DEBUG(4,(" session setup ok\n")); + + if (!cli_send_tconX(c, sharename, "?????", password)) { + 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); + 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, lp_netbios_name(), 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_state_init()) || !cli_socket_connect(cli, server_name, &ip)) { + d_printf("Connection to %s failed\n", desthost); + return 1; + } + + if (!cli_transport_establish(cli, &calling, &called)) { + d_printf("session request failed\n"); + cli_shutdown(cli); + return 1; + } + + send_message(); + cli_shutdown(cli); + + return 0; +} + + +/** + * Process "-L hostname" option. + * + * We don't actually do anything yet -- we just stash the name in a + * global variable and do the query when all options have been read. + **/ +static void remember_query_host(const char *arg, + pstring query_host) +{ + char *slash; + + while (*arg == '\\' || *arg == '/') + arg++; + pstrcpy(query_host, arg); + if ((slash = strchr(query_host, '/')) + || (slash = strchr(query_host, '\\'))) { + *slash = 0; + } +} + + +/**************************************************************************** + main program +****************************************************************************/ + int main(int argc,char *argv[]) +{ + fstring base_directory; + int opt; + pstring query_host; + BOOL message = False; + extern char tar_type; + pstring term_code; + poptContext pc; + char *p; + int rc = 0; + TALLOC_CTX *mem_ctx; + struct poptOption long_options[] = { + POPT_AUTOHELP + + { "name-resolve", 'R', POPT_ARG_STRING, NULL, '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", "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, NULL, 'b', "Changes the transmit/send buffer", "BYTES" }, + { "port", 'p', POPT_ARG_INT, &port, 'p', "Port to connect to", "PORT" }, + POPT_COMMON_SAMBA + POPT_COMMON_CONNECTION + POPT_COMMON_CREDENTIALS + POPT_TABLEEND + }; + + +#ifdef KANJI + pstrcpy(term_code, KANJI); +#else /* KANJI */ + *term_code = 0; +#endif /* KANJI */ + + *query_host = 0; + *base_directory = 0; + + setup_logging(argv[0],True); + mem_ctx = talloc_init("client.c/main"); + if (!mem_ctx) { + d_printf("\nclient.c: Not enough memory\n"); + exit(1); + } + + 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); + } + + pc = poptGetContext("smbclient", argc, (const char **) argv, long_options, + POPT_CONTEXT_KEEP_FIRST); + poptSetOtherOptionHelp(pc, "service "); + + 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(mem_ctx, poptGetOptArg(pc)); + if (is_zero_ip(dest_ip)) + exit(1); + have_ip = True; + } + break; + case 'E': + setup_logging("client", DEBUG_STDERR); + break; + + case 'L': + remember_query_host(poptGetOptArg(pc), query_host); + break; + case 't': + pstrcpy(term_code, poptGetOptArg(pc)); + break; + case 'm': + lp_set_cmdline("max protocol", poptGetOptArg(pc)); + break; + case 'R': + lp_set_cmdline("name resolve order", poptGetOptArg(pc)); + break; + case 'T': + if (!tar_parseargs(argc, argv, poptGetOptArg(pc), optind)) { + poptPrintUsage(pc, stderr, 0); + exit(1); + } + break; + case 'D': + fstrcpy(base_directory,poptGetOptArg(pc)); + break; + case 'b': + io_bufsize = MAX(1, atoi(poptGetOptArg(pc))); + break; + } + } + + poptGetArg(pc); + + load_interfaces(); + + 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 = True; + pstrcpy(cmdline_auth_info.password,poptGetArg(pc)); + } + + //init_names(); + + 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 ) ); + + talloc_destroy(mem_ctx); + if (tar_type) { + if (cmdstr) + process_command_string(cmdstr); + return do_tar_op(base_directory); + } + + if ((p=strchr_m(query_host,'#'))) { + *p = 0; + p++; + sscanf(p, "%x", &name_type); + } + + if (*query_host) { + return do_host_query(query_host); + } + + if (message) { + return do_message_op(); + } + + if (process(base_directory)) { + return 1; + } + + return rc; +} diff --git a/source4/client/clitar.c b/source4/client/clitar.c new file mode 100644 index 0000000000..78f44da8ca --- /dev/null +++ b/source4/client/clitar.c @@ -0,0 +1,1856 @@ +/* + 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); +void dos_clean_name(char *s); + +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 = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN; + +#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, int 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); + if (l >= NAMSIZ - 1) { + /* 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); + 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); + } + + /* use l + 1 to do the null too */ + fixtarname(hb.dbuf.name, aname, (l >= NAMSIZ) ? NAMSIZ : l + 1); + + if (lowercase) + strlower(hb.dbuf.name); + + /* write out a "standard" tar format header */ + + hb.dbuf.name[NAMSIZ-1]='\0'; + safe_strcpy(hb.dbuf.mode, amode, strlen(amode)); + 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); + 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=FILE_ATTRIBUTE_DIRECTORY; + } + 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, int l) +{ + /* add a '.' to start of file name, convert from ugly dos \'s in path + * to lovely unix /'s :-} */ + *tptr++='.'; + + safe_strcpy(tptr, fp, l); + 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) { + size_t size; + if (!cli_getattrE(cli, fnum, &finfo.mode, &size, NULL, &finfo.atime, &finfo.mtime)) { + DEBUG(0, ("getattrE: %s\n", cli_errstr(cli))); + return; + } + finfo.size = size; + finfo.ctime = finfo.mtime; + } + + DEBUG(3,("file %s attrib 0x%X\n",finfo.name,finfo.mode)); + + if (tar_inc && !(finfo.mode & FILE_ATTRIBUTE_ARCHIVE)) + { + DEBUG(4, ("skipping %s - archive bit not set\n", finfo.name)); + shallitime=0; + } + else if (!tar_system && (finfo.mode & FILE_ATTRIBUTE_SYSTEM)) + { + DEBUG(4, ("skipping %s - system bit is set\n", finfo.name)); + shallitime=0; + } + else if (!tar_hidden && (finfo.mode & FILE_ATTRIBUTE_HIDDEN)) + { + 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, FILE_ATTRIBUTE_ARCHIVE, 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))); + + safe_strcpy(exclaim, cur_dir, sizeof(pstring)); + *(exclaim+strlen(exclaim)-1)='\0'; + + safe_strcat(exclaim, "\\", sizeof(pstring)); + safe_strcat(exclaim, finfo->name, sizeof(exclaim)); + + 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(cli, exclaim, cliplist[0], True))) { +#endif + DEBUG(3,("Skipping file %s\n", exclaim)); + return; + } + } + + if (finfo->mode & FILE_ATTRIBUTE_DIRECTORY) + { + pstring saved_curdir; + pstring mtar_mask; + + safe_strcpy(saved_curdir, cur_dir, sizeof(saved_curdir)); + + 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)); + + safe_strcat(cur_dir,finfo->name, sizeof(cur_dir)); + safe_strcat(cur_dir,"\\", sizeof(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 */ + safe_strcpy(mtar_mask,cur_dir, sizeof(pstring)); + safe_strcat(mtar_mask,"*", sizeof(pstring)); + DEBUG(5, ("Doing list with mtar_mask: %s\n", mtar_mask)); + do_list(mtar_mask, attribute, do_tar, False, True); + safe_strcpy(cur_dir,saved_curdir, sizeof(pstring)); + } + else + { + safe_strcpy(rname,cur_dir, sizeof(pstring)); + safe_strcat(rname,finfo->name, sizeof(pstring)); + 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 + * 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, <arbuf[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, rsize = 0, bpos = 0; + + DEBUG(5, ("get_file: file: %s, size %i\n", finfo.name, (int)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 . + * 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 %d bytes\n", finfo.name, (int)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) +{ + 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 = %d\n", (int)finfo.size)); + + if (longname == NULL) { + + DEBUG(0, ("could not allocate buffer of size %d for longname\n", + (int)(finfo.size + strlen(cur_dir) + 2))); + 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(cli, finfo.name, cliplist[0], 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 + * 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")); + 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 <[+|-]rsha>\n")); + return 1; + } + + safe_strcpy(fname, cur_dir, sizeof(pstring)); + safe_strcat(fname, buf, sizeof(pstring)); + + 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]|=FILE_ATTRIBUTE_READONLY; + break; + case 'h': attra[direct]|=FILE_ATTRIBUTE_HIDDEN; + break; + case 's': attra[direct]|=FILE_ATTRIBUTE_SYSTEM; + break; + case 'a': attra[direct]|=FILE_ATTRIBUTE_ARCHIVE; + break; + default: DEBUG(0, ("setmode \n")); + return 1; + } + } + + if (attra[ATTRSET]==0 && attra[ATTRRESET]==0) + { + DEBUG(0, ("setmode <[+|-]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 [IXbgan] \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= 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) +{ + 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++; + } + 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)))); + 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; + } + if (! read_inclusion_file(argv[Optind+1])) { + return 0; + } + } else if (Optind+1=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) + setup_logging("clitar", DEBUG_STDERR); + } 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); + } + } + + return 1; +} diff --git a/source4/client/mount.cifs.c b/source4/client/mount.cifs.c new file mode 100644 index 0000000000..7167859d7b --- /dev/null +++ b/source4/client/mount.cifs.c @@ -0,0 +1,557 @@ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MOUNT_CIFS_VERSION "1" + +extern char *getusername(void); + +char * thisprogram; +int verboseflag = 0; +static int got_password = 0; +static int got_user = 0; +static int got_domain = 0; +static int got_ip = 0; +static int got_unc = 0; +static int got_uid = 0; +static int got_gid = 0; +static char * user_name = NULL; +char * mountpassword = NULL; + + +void mount_cifs_usage() +{ + printf("\nUsage: %s remotetarget dir\n", thisprogram); + printf("\nMount the remotetarget, specified as either a UNC name or "); + printf(" CIFS URL, to the local directory, dir.\n"); + + exit(1); +} + +/* caller frees username if necessary */ +char * getusername() { + char *username = NULL; + struct passwd *password = getpwuid(getuid()); + + if (password) { + username = password->pw_name; + } + return username; +} + +char * parse_cifs_url(unc_name) +{ + printf("\ncifs url %s\n",unc_name); +} + +int parse_options(char * options) +{ + char * data; + char * value = 0; + + if (!options) + return 1; + + while ((data = strsep(&options, ",")) != NULL) { + if (!*data) + continue; + if ((value = strchr(data, '=')) != NULL) { + *value++ = '\0'; + } + if (strncmp(data, "user", 4) == 0) { + if (!value || !*value) { + printf("invalid or missing username\n"); + return 1; /* needs_arg; */ + } + if (strnlen(value, 260) < 260) { + got_user=1; + /* BB add check for format user%pass */ + /* if(strchr(username%passw) got_password = 1) */ + } else { + printf("username too long\n"); + return 1; + } + } else if (strncmp(data, "pass", 4) == 0) { + if (!value || !*value) { + if(got_password) { + printf("password specified twice, ignoring second\n"); + } else + got_password = 1; + } else if (strnlen(value, 17) < 17) { + got_password = 1; + } else { + printf("password too long\n"); + return 1; + } + } else if (strncmp(data, "ip", 2) == 0) { + if (!value || !*value) { + printf("target ip address argument missing"); + } else if (strnlen(value, 35) < 35) { + got_ip = 1; + } else { + printf("ip address too long\n"); + return 1; + } + } else if ((strncmp(data, "unc", 3) == 0) + || (strncmp(data, "target", 6) == 0) + || (strncmp(data, "path", 4) == 0)) { + if (!value || !*value) { + printf("invalid path to network resource\n"); + return 1; /* needs_arg; */ + } else if(strnlen(value,5) < 5) { + printf("UNC name too short"); + } + + if (strnlen(value, 300) < 300) { + got_unc = 1; + if (strncmp(value, "//", 2) == 0) { + if(got_unc) + printf("unc name specified twice, ignoring second\n"); + else + got_unc = 1; + } else if (strncmp(value, "\\\\", 2) != 0) { + printf("UNC Path does not begin with // or \\\\ \n"); + return 1; + } else { + if(got_unc) + printf("unc name specified twice, ignoring second\n"); + else + got_unc = 1; + } + } else { + printf("CIFS: UNC name too long\n"); + return 1; + } + } else if ((strncmp(data, "domain", 3) == 0) + || (strncmp(data, "workgroup", 5) == 0)) { + if (!value || !*value) { + printf("CIFS: invalid domain name\n"); + return 1; /* needs_arg; */ + } + if (strnlen(value, 65) < 65) { + got_domain = 1; + } else { + printf("domain name too long\n"); + return 1; + } + } else if (strncmp(data, "uid", 3) == 0) { + if (value && *value) { + got_uid = 1; + } + } else if (strncmp(data, "gid", 3) == 0) { + if (value && *value) { + got_gid = 1; + } + } /* else if (strnicmp(data, "file_mode", 4) == 0) { + if (value && *value) { + vol->file_mode = + simple_strtoul(value, &value, 0); + } + } else if (strnicmp(data, "dir_mode", 3) == 0) { + if (value && *value) { + vol->dir_mode = + simple_strtoul(value, &value, 0); + } + } else if (strnicmp(data, "port", 4) == 0) { + if (value && *value) { + vol->port = + simple_strtoul(value, &value, 0); + } + } else if (strnicmp(data, "rsize", 5) == 0) { + if (value && *value) { + vol->rsize = + simple_strtoul(value, &value, 0); + } + } else if (strnicmp(data, "wsize", 5) == 0) { + if (value && *value) { + vol->wsize = + simple_strtoul(value, &value, 0); + } + } else if (strnicmp(data, "version", 3) == 0) { + + } else if (strnicmp(data, "rw", 2) == 0) { + + } else + printf("CIFS: Unknown mount option %s\n",data); */ + } + return 0; +} + +/* Note that caller frees the returned buffer if necessary */ +char * parse_server(char * unc_name) +{ + int length = strnlen(unc_name,1024); + char * share; + char * ipaddress_string = NULL; + struct hostent * host_entry; + struct in_addr server_ipaddr; + int rc,j; + char temp[64]; + + if(length > 1023) { + printf("mount error: UNC name too long"); + return 0; + } + if (strncasecmp("cifs://",unc_name,7) == 0) + return parse_cifs_url(unc_name+7); + if (strncasecmp("smb://",unc_name,6) == 0) { + return parse_cifs_url(unc_name+6); + } + + if(length < 3) { + /* BB add code to find DFS root here */ + printf("\nMounting the DFS root for domain not implemented yet"); + return 0; + } else { + /* BB add support for \\\\ not just // */ + if(strncmp(unc_name,"//",2) && strncmp(unc_name,"\\\\",2)) { + printf("mount error: improperly formatted UNC name."); + printf(" %s does not begin with \\\\ or //\n",unc_name); + return 0; + } else { + unc_name[0] = '\\'; + unc_name[1] = '\\'; + unc_name += 2; + if ((share = strchr(unc_name, '/')) || + (share = strchr(unc_name,'\\'))) { + *share = 0; /* temporarily terminate the string */ + share += 1; + host_entry = gethostbyname(unc_name); + *(share - 1) = '\\'; /* put the slash back */ +/* rc = getipnodebyname(unc_name, AF_INET, AT_ADDRCONFIG ,&rc);*/ + if(host_entry == NULL) { + printf("mount error: could not find target server. TCP name %s not found ", unc_name); + printf(" rc = %d\n",rc); + return 0; + } + else { + /* BB should we pass an alternate version of the share name as Unicode */ + /* BB what about ipv6? BB */ + /* BB add retries with alternate servers in list */ + + memcpy(&server_ipaddr.s_addr, host_entry->h_addr, 4); + + ipaddress_string = inet_ntoa(server_ipaddr); + if(ipaddress_string == NULL) { + printf("mount error: could not get valid ip address for target server\n"); + return 0; + } + return ipaddress_string; + } + } else { + /* BB add code to find DFS root (send null path on get DFS Referral to specified server here */ + printf("Mounting the DFS root for a particular server not implemented yet\n"); + return 0; + } + } + } +} + +static struct option longopts[] = { + { "all", 0, 0, 'a' }, + { "help", 0, 0, 'h' }, + { "read-only", 0, 0, 'r' }, + { "ro", 0, 0, 'r' }, + { "verbose", 0, 0, 'v' }, + { "version", 0, 0, 'V' }, + { "read-write", 0, 0, 'w' }, + { "rw", 0, 0, 'w' }, + { "options", 1, 0, 'o' }, + { "types", 1, 0, 't' }, + { "replace", 0, 0, 129 }, + { "after", 0, 0, 130 }, + { "before", 0, 0, 131 }, + { "over", 0, 0, 132 }, + { "move", 0, 0, 133 }, + { "rsize",1, 0, 136 }, + { "wsize",1, 0, 137 }, + { "uid", 1, 0, 138}, + { "gid", 1, 0, 139}, + { "uuid",1,0,'U' }, + { "user",1,0,140}, + { "username",1,0,140}, + { "dom",1,0,141}, + { "domain",1,0,141}, + { "password",1,0,142}, + { NULL, 0, 0, 0 } +}; + +int main(int argc, char ** argv) +{ + int c; + int flags = MS_MANDLOCK | MS_MGC_VAL; + char * orgoptions = NULL; + char * share_name = NULL; + char * domain_name = NULL; + char * ipaddr = NULL; + char * uuid = NULL; + char * mountpoint; + char * options; + int rc,i; + int rsize = 0; + int wsize = 0; + int nomtab = 0; + int uid = 0; + int gid = 0; + int optlen = 0; + struct stat statbuf; + struct utsname sysinfo; + struct mntent mountent; + FILE * pmntfile; + + /* setlocale(LC_ALL, ""); + 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\n", sysinfo.nodename,sysinfo.machine); +#endif*/ + if(argc < 3) + mount_cifs_usage(); + share_name = argv[1]; + mountpoint = argv[2]; + /* add sharename in opts string as unc= parm */ + + while ((c = getopt_long (argc, argv, "afFhilL:no:O:rsU:vVwt:", + longopts, NULL)) != -1) { + switch (c) { +/* case 'a': + ++mount_all; + break; + case 'f': + ++fake; + break; + case 'F': + ++optfork; + break; */ + case 'h': /* help */ + mount_cifs_usage (); + break; +/* case 'i': + external_allowed = 0; + break; + case 'l': + list_with_volumelabel = 1; + break; + case 'L': + volumelabel = optarg; + break; */ + case 'n': + ++nomtab; + break; + case 'o': + if (orgoptions) { + orgoptions = strcat(orgoptions, ","); + orgoptions = strcat(orgoptions,optarg); + } else + orgoptions = strdup(optarg); + break; + +/* case 'O': + if (test_opts) + test_opts = xstrconcat3(test_opts, ",", optarg); + else + test_opts = xstrdup(optarg); + break;*/ + case 'r': /* mount readonly */ + flags |= MS_RDONLY;; + break; + case 'U': + uuid = optarg; + break; + case 'v': + ++verboseflag; + break; +/* case 'V': + printf ("mount: %s\n", version); + exit (0);*/ + case 'w': + flags &= ~MS_RDONLY;; + break; +/* case 0: + break; + + case 128: + mounttype = MS_BIND; + break; + case 129: + mounttype = MS_REPLACE; + break; + case 130: + mounttype = MS_AFTER; + break; + case 131: + mounttype = MS_BEFORE; + break; + case 132: + mounttype = MS_OVER; + break; + case 133: + mounttype = MS_MOVE; + break; + case 135: + mounttype = (MS_BIND | MS_REC); + break; */ + case 136: + rsize = atoi(optarg) ; + break; + case 137: + wsize = atoi(optarg); + break; + case 138: + uid = atoi(optarg); + break; + case 139: + gid = atoi(optarg); + break; + case 140: + got_user = 1; + user_name = optarg; + break; + case 141: + domain_name = optarg; + break; + case 142: + got_password = 1; + mountpassword = optarg; + break; + case '?': + default: + mount_cifs_usage (); + } + } + + /* canonicalize the path in argv[1]? */ + + if(stat (mountpoint, &statbuf)) { + printf("mount error: mount point %s does not exist\n",mountpoint); + return -1; + } + if (S_ISDIR(statbuf.st_mode) == 0) { + printf("mount error: mount point %s is not a directory\n",mountpoint); + return -1; + } + + if(geteuid()) { + printf("mount error: permission denied, not superuser and cifs.mount not installed SUID\n"); + return -1; + } + + ipaddr = parse_server(share_name); +/* if(share_name == NULL) + return 1; */ + if (parse_options(strdup(orgoptions))) + return 1; + + if(got_user == 0) + user_name = getusername(); + +/* check username for user%password format */ + + if(got_password == 0) { + if (getenv("PASSWD")) { + mountpassword = malloc(33); + if(mountpassword) { + strncpy(mountpassword,getenv("PASSWD"),32); + got_password = 1; + } +/* } else if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) { + get_password_file(); + got_password = 1;*/ /* BB add missing function */ + } else { + mountpassword = getpass("Password: "); /* BB obsolete */ + got_password = 1; + } + } + /* FIXME launch daemon (handles dfs name resolution and credential change) + remember to clear parms and overwrite password field before launching */ + if(orgoptions) { + optlen = strlen(orgoptions); + } else + optlen = 0; + if(share_name) + optlen += strlen(share_name) + 4; + if(user_name) + optlen += strlen(user_name) + 6; + if(ipaddr) + optlen += strlen(ipaddr) + 4; + if(mountpassword) + optlen += strlen(mountpassword) + 6; + options = malloc(optlen + 10); + + options[0] = 0; + strncat(options,"unc=",4); + strcat(options,share_name); + if(ipaddr) { + strncat(options,",ip=",4); + strcat(options,ipaddr); + } + if(user_name) { + strncat(options,",user=",6); + strcat(options,user_name); + } + if(mountpassword) { + strncat(options,",pass=",6); + strcat(options,mountpassword); + } + strncat(options,",ver=",5); + strcat(options,MOUNT_CIFS_VERSION); + + if(orgoptions) { + strcat(options,","); + strcat(options,orgoptions); + } + /* printf("\noptions %s \n",options);*/ + if(mount(share_name, mountpoint, "cifs", flags, options)) { + /* remember to kill daemon on error */ + switch (errno) { + case 0: + printf("mount failed but no error number set\n"); + return 0; + case ENODEV: + printf("mount error: cifs filesystem not supported by the system\n"); + break; + default: + printf("mount error %d = %s",errno,strerror(errno)); + } + printf("Refer to the mount.cifs(8) manual page (e.g.man mount.cifs)\n"); + return -1; + } else { + pmntfile = setmntent(MOUNTED, "a+"); + if(pmntfile) { + mountent.mnt_fsname = share_name; + mountent.mnt_dir = mountpoint; + mountent.mnt_type = "cifs"; + mountent.mnt_opts = ""; + mountent.mnt_freq = 0; + mountent.mnt_passno = 0; + rc = addmntent(pmntfile,&mountent); + endmntent(pmntfile); + } else { + printf("could not update mount table\n"); + } + } + return 0; +} + diff --git a/source4/client/smbmnt.c b/source4/client/smbmnt.c new file mode 100644 index 0000000000..ce406179cf --- /dev/null +++ b/source4/client/smbmnt.c @@ -0,0 +1,306 @@ +/* + * smbmnt.c + * + * Copyright (C) 1995-1998 by Paal-Kr. Engstad and Volker Lendecke + * extensively modified by Tridge + * + */ + +#include "includes.h" + +#include +#include + +#include +#include +#include +#include +#include + +#ifndef MS_MGC_VAL +/* This may look strange but MS_MGC_VAL is what we are looking for and + is what we need from under libc systems and is + provided in standard includes on glibc systems. So... We + switch on what we need... */ +#include +#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",VERSION); + printf("-s share share name on server\n" + "-r mount read-only\n" + "-u uid mount as uid\n" + "-g gid mount as gid\n" + "-f mask permission mask for files\n" + "-d mask permission mask for directories\n" + "-o options name=value, list of options\n" + "-h print this help text\n"); +} + +static int +parse_args(int argc, char *argv[], struct smb_mount_data *data, char **share) +{ + int opt; + + while ((opt = getopt (argc, argv, "s:u:g:rf:d:o:")) != EOF) + { + switch (opt) + { + case 's': + *share = optarg; + break; + case 'u': + if (!user_mount) { + mount_uid = strtol(optarg, NULL, 0); + } + break; + case 'g': + if (!user_mount) { + mount_gid = strtol(optarg, NULL, 0); + } + break; + case 'r': + mount_ro = 1; + break; + case 'f': + mount_fmask = strtol(optarg, NULL, 8); + break; + case 'd': + mount_dmask = strtol(optarg, NULL, 8); + break; + case 'o': + options = optarg; + break; + default: + return -1; + } + } + return 0; + +} + +static char * +fullpath(const char *p) +{ + char path[MAXPATHLEN]; + + if (strlen(p) > MAXPATHLEN-1) { + return NULL; + } + + if (realpath(p, path) == NULL) { + fprintf(stderr,"Failed to find real path for mount point\n"); + exit(1); + } + return strdup(path); +} + +/* Check whether user is allowed to mount on the specified mount point. If it's + OK then we change into that directory - this prevents race conditions */ +static int mount_ok(char *mount_point) +{ + struct stat st; + + if (chdir(mount_point) != 0) { + return -1; + } + + if (stat(".", &st) != 0) { + return -1; + } + + if (!S_ISDIR(st.st_mode)) { + errno = ENOTDIR; + return -1; + } + + if ((getuid() != 0) && + ((getuid() != st.st_uid) || + ((st.st_mode & S_IRWXU) != S_IRWXU))) { + errno = EPERM; + return -1; + } + + return 0; +} + +/* Tries to mount using the appropriate format. For 2.2 the struct, + for 2.4 the ascii version. */ +static int +do_mount(char *share_name, 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; + + if (mount_ro) flags |= MS_RDONLY; + + if (do_mount(share_name, flags, &data) < 0) { + switch (errno) { + case ENODEV: + fprintf(stderr, "ERROR: smbfs filesystem not supported by the kernel\n"); + break; + default: + perror("mount error"); + } + fprintf(stderr, "Please refer to the smbmnt(8) manual page\n"); + return -1; + } + + ment.mnt_fsname = share_name ? share_name : "none"; + ment.mnt_dir = mount_point; + ment.mnt_type = "smbfs"; + ment.mnt_opts = ""; + ment.mnt_freq = 0; + ment.mnt_passno= 0; + + mount_point = ment.mnt_dir; + + if (mount_point == NULL) + { + fprintf(stderr, "Mount point too long\n"); + return -1; + } + + if ((fd = open(MOUNTED"~", O_RDWR|O_CREAT|O_EXCL, 0600)) == -1) + { + fprintf(stderr, "Can't get "MOUNTED"~ lock file"); + return 1; + } + close(fd); + + if ((mtab = setmntent(MOUNTED, "a+")) == NULL) + { + fprintf(stderr, "Can't open " MOUNTED); + return 1; + } + + if (addmntent(mtab, &ment) == 1) + { + fprintf(stderr, "Can't write mount entry"); + return 1; + } + if (fchmod(fileno(mtab), 0644) == -1) + { + fprintf(stderr, "Can't set perms on "MOUNTED); + return 1; + } + endmntent(mtab); + + if (unlink(MOUNTED"~") == -1) + { + fprintf(stderr, "Can't remove "MOUNTED"~"); + return 1; + } + + return 0; +} diff --git a/source4/client/smbmount.c b/source4/client/smbmount.c new file mode 100644 index 0000000000..da340144fc --- /dev/null +++ b/source4/client/smbmount.c @@ -0,0 +1,930 @@ +/* + 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. +*/ + +#include "includes.h" + +#include +#include +#include + +extern BOOL in_client; + +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 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. */ + if (status32_smbfs) { + c->capabilities &= ~(CAP_UNICODE | CAP_LARGE_FILES | CAP_NT_SMBS | + CAP_NT_FIND | CAP_LEVEL_II_OPLOCKS); + } + else { + c->capabilities &= ~(CAP_UNICODE | CAP_LARGE_FILES | CAP_NT_SMBS | + CAP_NT_FIND | CAP_STATUS32 | + CAP_LEVEL_II_OPLOCKS); + c->force_dos_errors = True; + } + + if (!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"); /* 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[MAXPATHLEN+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",VERSION); + + printf( +"Options:\n\ + username= SMB username\n\ + password= SMB password\n\ + credentials= file with username/password\n\ + krb use kerberos (active directory)\n\ + netbiosname= source NetBIOS name\n\ + uid= mount uid or username\n\ + gid= mount gid or groupname\n\ + port= remote SMB port number\n\ + fmask= file umask\n\ + dmask= directory umask\n\ + debug= debug level\n\ + ip= destination host or IP address\n\ + workgroup= workgroup on destination\n\ + sockopt= TCP socket options\n\ + scope= NetBIOS scope\n\ + iocharset= Linux charset (iso8859-1, utf8)\n\ + codepage= server codepage (cp850)\n\ + ttl= 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 + + 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")) { + lp_set_cmdline("socket options", opteq+1); + } else if(!strcmp(opts, "scope")) { + lp_set_cmdline("netbios scope", opteq+1); + } else { + slprintf(p, sizeof(pstring) - (p - options) - 1, "%s=%s,", opts, opteq+1); + p += strlen(p); + } + } else { + val = 1; + if(!strcmp(opts, "nocaps")) { + fprintf(stderr, "Unhandled option: %s\n", opteq+1); + exit(1); + } else if(!strcmp(opts, "guest")) { + *password = '\0'; + got_pass = True; + } else if(!strcmp(opts, "krb")) { +#ifdef HAVE_KRB5 + + use_kerberos = True; + if(!status32_smbfs) + fprintf(stderr, "Warning: kerberos support will only work for samba servers\n"); +#else + fprintf(stderr,"No kerberos support compiled in\n"); + exit(1); +#endif + } else if(!strcmp(opts, "rw")) { + mount_ro = 0; + } else if(!strcmp(opts, "ro")) { + mount_ro = 1; + } else { + strncpy(p, opts, sizeof(pstring) - (p - options) - 1); + p += strlen(opts); + *p++ = ','; + *p = 0; + } + } + } + + if (!*service) { + usage(); + exit(1); + } + + if (p != options) { + *(p-1) = 0; /* remove trailing , */ + DEBUG(3,("passthrough options '%s'\n", options)); + } +} + +/**************************************************************************** + main program +****************************************************************************/ + int main(int argc,char *argv[]) +{ + extern char *optarg; + extern int optind; + char *p; + + 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(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", VERSION)); + + if (*workgroup == 0) { + pstrcpy(workgroup,lp_workgroup()); + } + + load_interfaces(); + if (!*my_netbios_name) { + pstrcpy(my_netbios_name, myhostname()); + } + strupper(my_netbios_name); + + init_mount(); + return 0; +} diff --git a/source4/client/smbspool.c b/source4/client/smbspool.c new file mode 100644 index 0000000000..43046bbad7 --- /dev/null +++ b/source4/client/smbspool.c @@ -0,0 +1,362 @@ +/* + 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. +*/ + +#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 */ + char *myname; /* Client name */ + NTSTATUS nt_status; + + /* + * Get the names and addresses of the client and server... + */ + + myname = get_myname(); + + nt_status = cli_full_connection(&c, myname, server, NULL, 0, share, "?????", + username, workgroup, password, 0, NULL); + + free(myname); + 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/source4/client/smbumount.c b/source4/client/smbumount.c new file mode 100644 index 0000000000..9ea3083a6f --- /dev/null +++ b/source4/client/smbumount.c @@ -0,0 +1,186 @@ +/* + * smbumount.c + * + * Copyright (C) 1995-1998 by Volker Lendecke + * + */ + +#include "includes.h" + +#include + +#include +#include +#include +#include +#include + +/* This is a (hopefully) temporary hack due to the fact that + sizeof( uid_t ) != sizeof( __kernel_uid_t ) under glibc. + This may change in the future and smb.h may get fixed in the + future. In the mean time, it's ugly hack time - get over it. +*/ +#undef SMB_IOC_GETMOUNTUID +#define SMB_IOC_GETMOUNTUID _IOR('u', 1, __kernel_uid_t) + +#ifndef O_NOFOLLOW +#define O_NOFOLLOW 0400000 +#endif + +static void +usage(void) +{ + printf("usage: smbumount mountpoint\n"); +} + +static int +umount_ok(const char *mount_point) +{ + /* we set O_NOFOLLOW to prevent users playing games with symlinks to + umount filesystems they don't own */ + int fid = open(mount_point, O_RDONLY|O_NOFOLLOW, 0); + __kernel_uid_t mount_uid; + + if (fid == -1) { + fprintf(stderr, "Could not open %s: %s\n", + mount_point, strerror(errno)); + return -1; + } + + if (ioctl(fid, SMB_IOC_GETMOUNTUID, &mount_uid) != 0) { + fprintf(stderr, "%s probably not smb-filesystem\n", + mount_point); + return -1; + } + + if ((getuid() != 0) + && (mount_uid != getuid())) { + fprintf(stderr, "You are not allowed to umount %s\n", + mount_point); + return -1; + } + + close(fid); + return 0; +} + +/* Make a canonical pathname from PATH. Returns a freshly malloced string. + It is up the *caller* to ensure that the PATH is sensible. i.e. + canonicalize ("/dev/fd0/.") returns "/dev/fd0" even though ``/dev/fd0/.'' + is not a legal pathname for ``/dev/fd0'' Anything we cannot parse + we return unmodified. */ +static char * +canonicalize (char *path) +{ + char *canonical = malloc (PATH_MAX + 1); + + if (!canonical) { + fprintf(stderr, "Error! Not enough memory!\n"); + return NULL; + } + + if (strlen(path) > PATH_MAX) { + fprintf(stderr, "Mount point string too long\n"); + return NULL; + } + + if (path == NULL) + return NULL; + + if (realpath (path, canonical)) + return canonical; + + strncpy (canonical, path, PATH_MAX); + canonical[PATH_MAX] = '\0'; + return canonical; +} + + +int +main(int argc, char *argv[]) +{ + int fd; + char* mount_point; + struct mntent *mnt; + FILE* mtab; + FILE* new_mtab; + + if (argc != 2) { + usage(); + exit(1); + } + + if (geteuid() != 0) { + fprintf(stderr, "smbumount must be installed suid root\n"); + exit(1); + } + + mount_point = canonicalize(argv[1]); + + if (mount_point == NULL) + { + exit(1); + } + + if (umount_ok(mount_point) != 0) { + exit(1); + } + + if (umount(mount_point) != 0) { + fprintf(stderr, "Could not umount %s: %s\n", + mount_point, strerror(errno)); + exit(1); + } + + if ((fd = open(MOUNTED"~", O_RDWR|O_CREAT|O_EXCL, 0600)) == -1) + { + fprintf(stderr, "Can't get "MOUNTED"~ lock file"); + return 1; + } + close(fd); + + if ((mtab = setmntent(MOUNTED, "r")) == NULL) { + fprintf(stderr, "Can't open " MOUNTED ": %s\n", + strerror(errno)); + return 1; + } + +#define MOUNTED_TMP MOUNTED".tmp" + + if ((new_mtab = setmntent(MOUNTED_TMP, "w")) == NULL) { + fprintf(stderr, "Can't open " MOUNTED_TMP ": %s\n", + strerror(errno)); + endmntent(mtab); + return 1; + } + + while ((mnt = getmntent(mtab)) != NULL) { + if (strcmp(mnt->mnt_dir, mount_point) != 0) { + addmntent(new_mtab, mnt); + } + } + + endmntent(mtab); + + if (fchmod (fileno (new_mtab), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) { + fprintf(stderr, "Error changing mode of %s: %s\n", + MOUNTED_TMP, strerror(errno)); + exit(1); + } + + endmntent(new_mtab); + + if (rename(MOUNTED_TMP, MOUNTED) < 0) { + fprintf(stderr, "Cannot rename %s to %s: %s\n", + MOUNTED, MOUNTED_TMP, strerror(errno)); + exit(1); + } + + if (unlink(MOUNTED"~") == -1) + { + fprintf(stderr, "Can't remove "MOUNTED"~"); + return 1; + } + + return 0; +} diff --git a/source4/client/tree.c b/source4/client/tree.c new file mode 100644 index 0000000000..94fd93c210 --- /dev/null +++ b/source4/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 +#include +#include +#include "libsmbclient.h" + +static GtkWidget *clist; + +struct tree_data { + + guint32 type; /* Type of tree item, an SMBC_TYPE */ + char name[256]; /* May need to change this later */ + +}; + +void error_message(gchar *message) { + + GtkWidget *dialog, *label, *okay_button; + + /* Create the widgets */ + + dialog = gtk_dialog_new(); + gtk_window_set_modal(GTK_WINDOW(dialog), True); + label = gtk_label_new (message); + okay_button = gtk_button_new_with_label("Okay"); + + /* Ensure that the dialog box is destroyed when the user clicks ok. */ + + gtk_signal_connect_object (GTK_OBJECT (okay_button), "clicked", + GTK_SIGNAL_FUNC (gtk_widget_destroy), dialog); + gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->action_area), + okay_button); + + /* Add the label, and show everything we've added to the dialog. */ + + gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->vbox), + label); + gtk_widget_show_all (dialog); +} + +/* + * We are given a widget, and we want to retrieve its URL so we + * can do a directory listing. + * + * We walk back up the tree, picking up pieces until we hit a server or + * workgroup type and return a path from there + */ + +static char path_string[1024]; + +char *get_path(GtkWidget *item) +{ + GtkWidget *p = item; + struct tree_data *pd; + char *comps[1024]; /* We keep pointers to the components here */ + int i = 0, j, level,type; + + /* Walk back up the tree, getting the private data */ + + level = GTK_TREE(item->parent)->level; + + /* Pick up this item's component info */ + + pd = (struct tree_data *)gtk_object_get_user_data(GTK_OBJECT(item)); + + comps[i++] = pd->name; + type = pd->type; + + while (level > 0 && type != SMBC_SERVER && type != SMBC_WORKGROUP) { + + /* Find the parent and extract the data etc ... */ + + p = GTK_WIDGET(p->parent); + p = GTK_WIDGET(GTK_TREE(p)->tree_owner); + + pd = (struct tree_data *)gtk_object_get_user_data(GTK_OBJECT(p)); + + level = GTK_TREE(item->parent)->level; + + comps[i++] = pd->name; + type = pd->type; + + } + + /* + * Got a list of comps now, should check that we did not hit a workgroup + * when we got other things as well ... Later + * + * Now, build the path + */ + + snprintf(path_string, sizeof(path_string), "smb:/"); + + for (j = i - 1; j >= 0; j--) { + + strncat(path_string, "/", sizeof(path_string) - strlen(path_string)); + strncat(path_string, comps[j], sizeof(path_string) - strlen(path_string)); + + } + + fprintf(stdout, "Path string = %s\n", path_string); + + return path_string; + +} + +struct tree_data *make_tree_data(guint32 type, const char *name) +{ + struct tree_data *p = (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; + char path[1024], path1[1024]; + + g_print ("select_child called for root tree %p, subtree %p, child %p\n", + root_tree, subtree, child); + + /* Now, figure out what it is, and display it in the clist ... */ + + gtk_clist_clear(GTK_CLIST(clist)); /* Clear the CLIST */ + + /* Now, get the private data for the subtree */ + + strncpy(path, get_path(child), 1024); + + if ((dh = smbc_opendir(path)) < 0) { /* Handle error */ + + g_print("cb_select_child: Could not open dir %s, %s\n", path, + strerror(errno)); + + gtk_main_quit(); + + return; + + } + + while ((err = smbc_getdents(dh, (struct smbc_dirent *)dirbuf, + sizeof(dirbuf))) != 0) { + + if (err < 0) { + + g_print("cb_select_child: Could not read dir %s, %s\n", path, + strerror(errno)); + + gtk_main_quit(); + + return; + + } + + dirp = (struct smbc_dirent *)dirbuf; + + while (err > 0) { + gchar col1[128], col2[128], col3[128], col4[128]; + gchar *rowdata[4] = {col1, col2, col3, col4}; + + dirlen = dirp->dirlen; + + /* Format each of the items ... */ + + strncpy(col1, dirp->name, 128); + + col2[0] = col3[0] = col4[0] = (char)0; + + switch (dirp->smbc_type) { + + case SMBC_WORKGROUP: + + break; + + case SMBC_SERVER: + + strncpy(col2, (dirp->comment?dirp->comment:""), 128); + + break; + + case SMBC_FILE_SHARE: + + strncpy(col2, (dirp->comment?dirp->comment:""), 128); + + break; + + case SMBC_PRINTER_SHARE: + + strncpy(col2, (dirp->comment?dirp->comment:""), 128); + break; + + case SMBC_COMMS_SHARE: + + break; + + case SMBC_IPC_SHARE: + + break; + + case SMBC_DIR: + case SMBC_FILE: + + /* Get stats on the file/dir and see what we have */ + + if ((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)); + + error_message(errmsg); + + /* gtk_main_quit();*/ + + return; + + } + + while ((err = smbc_getdents(dh, (struct smbc_dirent *)dirbuf, + sizeof(dirbuf))) != 0) { + + if (err < 0) { /* An error, report it */ + gchar errmsg[256]; + + g_print("cb_itemsignal: Could not read dir smbc://, %s\n", + strerror(errno)); + + slprintf(errmsg, sizeof(errmsg), "cb_itemsignal: Could not read dir smbc://, %s\n", strerror(errno)); + + error_message(errmsg); + + /* gtk_main_quit();*/ + + return; + + } + + dirp = (struct smbc_dirent *)dirbuf; + + while (err > 0) { + struct tree_data *my_data; + + dirlen = dirp->dirlen; + + my_data = make_tree_data(dirp->smbc_type, dirp->name); + + if (!my_data) { + + g_print("Could not allocate space for tree_data: %s\n", + dirp->name); + + gtk_main_quit(); + return; + + } + + aitem = gtk_tree_item_new_with_label(dirp->name); + + /* Connect all GtkItem:: and GtkTreeItem:: signals */ + gtk_signal_connect (GTK_OBJECT(aitem), "select", + GTK_SIGNAL_FUNC(cb_itemsignal), "select"); + gtk_signal_connect (GTK_OBJECT(aitem), "deselect", + GTK_SIGNAL_FUNC(cb_itemsignal), "deselect"); + gtk_signal_connect (GTK_OBJECT(aitem), "toggle", + GTK_SIGNAL_FUNC(cb_itemsignal), "toggle"); + gtk_signal_connect (GTK_OBJECT(aitem), "expand", + GTK_SIGNAL_FUNC(cb_itemsignal), "expand"); + gtk_signal_connect (GTK_OBJECT(aitem), "collapse", + GTK_SIGNAL_FUNC(cb_itemsignal), "collapse"); + /* Add it to the parent tree */ + gtk_tree_append (GTK_TREE(real_tree), aitem); + + gtk_widget_show (aitem); + + gtk_object_set_user_data(GTK_OBJECT(aitem), (gpointer)my_data); + + fprintf(stdout, "Added: %s, len: %u\n", dirp->name, dirlen); + + if (dirp->smbc_type != SMBC_FILE && + dirp->smbc_type != SMBC_IPC_SHARE && + (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/source4/codepages/.cvsignore b/source4/codepages/.cvsignore new file mode 100644 index 0000000000..e69de29bb2 diff --git a/source4/codepages/valid.dat b/source4/codepages/valid.dat new file mode 100644 index 0000000000..78c14b33f0 Binary files /dev/null and b/source4/codepages/valid.dat differ diff --git a/source4/config.sub b/source4/config.sub new file mode 100755 index 0000000000..04baf3d80d --- /dev/null +++ b/source4/config.sub @@ -0,0 +1,1473 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +# 2000, 2001, 2002, 2003 Free Software Foundation, Inc. + +timestamp='2003-01-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 . 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 ." + +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* | freebsd*-gnu* | netbsd*-gnu* | storm-chaos* | os2-emx* | rtmk-nova*) + os=-$maybe_os + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + ;; + *) + basic_machine=`echo $1 | sed 's/-[^-]*$//'` + if [ $basic_machine != $1 ] + then os=`echo $1 | sed 's/.*-/-/'` + else os=; fi + ;; +esac + +### Let's recognize common machines as not being operating systems so +### that things like config.sub decstation-3100 work. We also +### recognize some manufacturers as not being operating systems, so we +### can provide default operating systems below. +case $os in + -sun*os*) + # Prevent following clause from handling this invalid input. + ;; + -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ + -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ + -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ + -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ + -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ + -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ + -apple | -axis) + os= + basic_machine=$1 + ;; + -sim | -cisco | -oki | -wec | -winbond) + os= + basic_machine=$1 + ;; + -scout) + ;; + -wrs) + os=-vxworks + basic_machine=$1 + ;; + -chorusos*) + os=-chorusos + basic_machine=$1 + ;; + -chorusrdb) + os=-chorusrdb + basic_machine=$1 + ;; + -hiux*) + os=-hiuxwe2 + ;; + -sco5) + os=-sco3.2v5 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco4) + os=-sco3.2v4 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2v[4-9]*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco*) + os=-sco3.2v2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -udk*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -isc) + os=-isc2.2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -clix*) + basic_machine=clipper-intergraph + ;; + -isc*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -lynx*) + os=-lynxos + ;; + -ptx*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` + ;; + -windowsnt*) + os=`echo $os | sed -e 's/windowsnt/winnt/'` + ;; + -psos*) + os=-psos + ;; + -mint | -mint[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; +esac + +# Decode aliases for certain CPU-COMPANY combinations. +case $basic_machine in + # Recognize the basic CPU types without company name. + # Some are omitted here because they have special meanings below. + 1750a | 580 \ + | a29k \ + | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ + | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ + | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \ + | clipper \ + | d10v | d30v | dlx | dsp16xx \ + | fr30 | frv \ + | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | i370 | i860 | i960 | ia64 \ + | ip2k \ + | m32r | m68000 | m68k | m88k | mcore \ + | mips | mipsbe | mipseb | mipsel | mipsle \ + | mips16 \ + | mips64 | mips64el \ + | mips64vr | mips64vrel \ + | mips64orion | mips64orionel \ + | mips64vr4100 | mips64vr4100el \ + | mips64vr4300 | mips64vr4300el \ + | mips64vr5000 | mips64vr5000el \ + | mipsisa32 | mipsisa32el \ + | mipsisa32r2 | mipsisa32r2el \ + | mipsisa64 | mipsisa64el \ + | mipsisa64sb1 | mipsisa64sb1el \ + | mipsisa64sr71k | mipsisa64sr71kel \ + | mipstx39 | mipstx39el \ + | mn10200 | mn10300 \ + | msp430 \ + | ns16k | ns32k \ + | openrisc | or32 \ + | pdp10 | pdp11 | pj | pjl \ + | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \ + | pyramid \ + | sh | sh[1234] | sh3e | sh[34]eb | shbe | shle | sh[1234]le | sh3ele \ + | sh64 | sh64le \ + | sparc | sparc64 | sparc86x | 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]-* \ + | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ + | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \ + | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ + | avr-* \ + | bs2000-* \ + | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* \ + | clipper-* | cydra-* \ + | d10v-* | d30v-* | dlx-* \ + | elxsi-* \ + | f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \ + | h8300-* | h8500-* \ + | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ + | i*86-* | i860-* | i960-* | ia64-* \ + | ip2k-* \ + | m32r-* \ + | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ + | m88110-* | m88k-* | mcore-* \ + | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ + | mips16-* \ + | mips64-* | mips64el-* \ + | mips64vr-* | mips64vrel-* \ + | mips64orion-* | mips64orionel-* \ + | mips64vr4100-* | mips64vr4100el-* \ + | mips64vr4300-* | mips64vr4300el-* \ + | mips64vr5000-* | mips64vr5000el-* \ + | mipsisa32-* | mipsisa32el-* \ + | mipsisa32r2-* | mipsisa32r2el-* \ + | mipsisa64-* | mipsisa64el-* \ + | mipsisa64sb1-* | mipsisa64sb1el-* \ + | mipsisa64sr71k-* | mipsisa64sr71kel-* \ + | mipstx39-* | mipstx39el-* \ + | msp430-* \ + | none-* | np1-* | nv1-* | ns16k-* | ns32k-* \ + | orion-* \ + | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ + | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \ + | pyramid-* \ + | romp-* | rs6000-* \ + | sh-* | sh[1234]-* | sh3e-* | sh[34]eb-* | shbe-* \ + | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ + | sparc-* | sparc64-* | sparc86x-* | sparclet-* | sparclite-* \ + | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* | sx?-* \ + | tahoe-* | thumb-* | tic30-* | tic4x-* | tic54x-* | tic80-* | tron-* \ + | v850-* | v850e-* | vax-* \ + | we32k-* \ + | x86-* | x86_64-* | 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 + ;; + c90) + basic_machine=c90-cray + os=-unicos + ;; + convex-c1) + basic_machine=c1-convex + os=-bsd + ;; + convex-c2) + basic_machine=c2-convex + os=-bsd + ;; + convex-c32) + basic_machine=c32-convex + os=-bsd + ;; + convex-c34) + basic_machine=c34-convex + os=-bsd + ;; + convex-c38) + basic_machine=c38-convex + os=-bsd + ;; + cray | j90) + basic_machine=j90-cray + os=-unicos + ;; + 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 + ;; + decsystem10* | dec10*) + basic_machine=pdp10-dec + os=-tops10 + ;; + decsystem20* | dec20*) + basic_machine=pdp10-dec + os=-tops20 + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + basic_machine=m68k-motorola + ;; + delta88) + basic_machine=m88k-motorola + os=-sysv3 + ;; + dpx20 | dpx20-*) + basic_machine=rs6000-bull + os=-bosx + ;; + dpx2* | dpx2*-bull) + basic_machine=m68k-bull + os=-sysv3 + ;; + ebmon29k) + basic_machine=a29k-amd + os=-ebmon + ;; + elxsi) + basic_machine=elxsi-elxsi + os=-bsd + ;; + encore | umax | mmax) + basic_machine=ns32k-encore + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + os=-ose + ;; + fx2800) + basic_machine=i860-alliant + ;; + genix) + basic_machine=ns32k-ns + ;; + gmicro) + basic_machine=tron-gmicro + os=-sysv + ;; + go32) + basic_machine=i386-pc + os=-go32 + ;; + h3050r* | hiux*) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=-hms + ;; + h8300xray) + basic_machine=h8300-hitachi + os=-xray + ;; + h8500hms) + basic_machine=h8500-hitachi + os=-hms + ;; + harris) + basic_machine=m88k-harris + os=-sysv3 + ;; + hp300-*) + basic_machine=m68k-hp + ;; + hp300bsd) + basic_machine=m68k-hp + os=-bsd + ;; + hp300hpux) + basic_machine=m68k-hp + os=-hpux + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + basic_machine=m68000-hp + ;; + hp9k3[2-9][0-9]) + basic_machine=m68k-hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + basic_machine=hppa1.1-hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hppa-next) + os=-nextstep3 + ;; + hppaosf) + basic_machine=hppa1.1-hp + os=-osf + ;; + hppro) + basic_machine=hppa1.1-hp + os=-proelf + ;; + i370-ibm* | ibm*) + basic_machine=i370-ibm + ;; +# I'm not sure what "Sysv32" means. Should this be sysv3.2? + i*86v32) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv32 + ;; + i*86v4*) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv4 + ;; + i*86v) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv + ;; + i*86sol2) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-solaris2 + ;; + i386mach) + basic_machine=i386-mach + os=-mach + ;; + i386-vsta | vsta) + basic_machine=i386-unknown + os=-vsta + ;; + iris | iris4d) + basic_machine=mips-sgi + case $os in + -irix*) + ;; + *) + os=-irix4 + ;; + esac + ;; + isi68 | isi) + basic_machine=m68k-isi + os=-sysv + ;; + m88k-omron*) + basic_machine=m88k-omron + ;; + magnum | m3230) + basic_machine=mips-mips + os=-sysv + ;; + merlin) + basic_machine=ns32k-utek + os=-sysv + ;; + mingw32) + basic_machine=i386-pc + os=-mingw32 + ;; + miniframe) + basic_machine=m68000-convergent + ;; + *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; + mips3*-*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` + ;; + mips3*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown + ;; + mmix*) + basic_machine=mmix-knuth + os=-mmixware + ;; + monitor) + basic_machine=m68k-rom68k + os=-coff + ;; + morphos) + basic_machine=powerpc-unknown + os=-morphos + ;; + msdos) + basic_machine=i386-pc + os=-msdos + ;; + mvs) + basic_machine=i370-ibm + os=-mvs + ;; + ncr3000) + basic_machine=i486-ncr + os=-sysv4 + ;; + netbsd386) + basic_machine=i386-unknown + os=-netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + os=-linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=-newsos + ;; + news1000) + basic_machine=m68030-sony + os=-newsos + ;; + news-3600 | risc-news) + basic_machine=mips-sony + os=-newsos + ;; + necv70) + basic_machine=v70-nec + os=-sysv + ;; + next | m*-next ) + basic_machine=m68k-next + case $os in + -nextstep* ) + ;; + -ns2*) + os=-nextstep2 + ;; + *) + os=-nextstep3 + ;; + esac + ;; + nh3000) + basic_machine=m68k-harris + os=-cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=-cxux + ;; + nindy960) + basic_machine=i960-intel + os=-nindy + ;; + mon960) + basic_machine=i960-intel + os=-mon960 + ;; + nonstopux) + basic_machine=mips-compaq + os=-nonstopux + ;; + np1) + basic_machine=np1-gould + ;; + nv1) + basic_machine=nv1-cray + os=-unicosmp + ;; + nsr-tandem) + basic_machine=nsr-tandem + ;; + op50n-* | op60c-*) + basic_machine=hppa1.1-oki + os=-proelf + ;; + or32 | or32-*) + basic_machine=or32-unknown + os=-coff + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + os=-ose + ;; + os68k) + basic_machine=m68k-none + os=-os68k + ;; + pa-hitachi) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + paragon) + basic_machine=i860-intel + os=-osf + ;; + pbd) + basic_machine=sparc-tti + ;; + pbb) + basic_machine=m68k-tti + ;; + pc532 | pc532-*) + basic_machine=ns32k-pc532 + ;; + pentium | p5 | k5 | k6 | nexgen | viac3) + basic_machine=i586-pc + ;; + pentiumpro | p6 | 6x86 | athlon | athlon_*) + basic_machine=i686-pc + ;; + pentiumii | pentium2) + 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 + ;; + sb1) + basic_machine=mipsisa64sb1-unknown + ;; + sb1el) + basic_machine=mipsisa64sb1el-unknown + ;; + 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=alphaev5-cray + os=-unicos + ;; + t90) + basic_machine=t90-cray + os=-unicos + ;; + tic4x | c4x*) + basic_machine=tic4x-unknown + os=-coff + ;; + tic54x | c54x*) + basic_machine=tic54x-unknown + os=-coff + ;; + tx39) + basic_machine=mipstx39-unknown + ;; + tx39el) + basic_machine=mipstx39el-unknown + ;; + toad1) + basic_machine=pdp10-xkl + os=-tops20 + ;; + tower | tower-32) + basic_machine=m68k-ncr + ;; + 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 + ;; + xps | xps100) + basic_machine=xps100-honeywell + ;; + ymp) + basic_machine=ymp-cray + os=-unicos + ;; + z8k-*-coff) + basic_machine=z8k-unknown + os=-sim + ;; + none) + basic_machine=none-none + os=-none + ;; + +# Here we handle the default manufacturer of certain CPU types. It is in +# some cases the only manufacturer, in others, it is the most popular. + w89k) + basic_machine=hppa1.1-winbond + ;; + op50n) + basic_machine=hppa1.1-oki + ;; + op60c) + basic_machine=hppa1.1-oki + ;; + romp) + basic_machine=romp-ibm + ;; + 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 | sh[1234]le | sh3ele) + basic_machine=sh-unknown + ;; + sh64) + basic_machine=sh64-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 + ;; + *-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* | -mks* | -rhapsody* | -darwin* | -opened* \ + | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ + | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ + | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ + | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ + | -powermax* | -dnix* | -microbsd*) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + -qnx*) + case $basic_machine in + x86-* | i*86-*) + ;; + *) + os=-nto$os + ;; + esac + ;; + -nto-qnx*) + ;; + -nto*) + os=`echo $os | sed -e 's|nto|nto-qnx|'` + ;; + -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ + | -windows* | -osx | -abug | -netware* | -os9* | -beos* \ + | -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 + ;; + -nova*) + os=-rtmk-nova + ;; + -ns2 ) + os=-nextstep2 + ;; + -nsk*) + os=-nsk + ;; + # Preserve the version number of sinix5. + -sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + -sinix*) + os=-sysv4 + ;; + -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 + ;; + # This must come before the *-dec entry. + pdp10-*) + os=-tops20 + ;; + pdp11-*) + os=-none + ;; + *-dec | vax-*) + os=-ultrix4.2 + ;; + m68*-apollo) + os=-domain + ;; + i386-sun) + os=-sunos4.0.2 + ;; + m68000-sun) + os=-sunos3 + # This also exists in the configure program, but was not the + # default. + # os=-sunos4 + ;; + m68*-cisco) + os=-aout + ;; + mips*-cisco) + os=-elf + ;; + mips*-*) + os=-elf + ;; + or32-*) + os=-coff + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=-sysv3 + ;; + sparc-* | *-sun) + os=-sunos4.1.1 + ;; + *-be) + os=-beos + ;; + *-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* | -windiss*) + vendor=wrs + ;; + -aux*) + vendor=apple + ;; + -hms*) + vendor=hitachi + ;; + -mpw* | -macos*) + vendor=apple + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + vendor=atari + ;; + -vos*) + vendor=stratus + ;; + esac + basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` + ;; +esac + +echo $basic_machine$os +exit 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/source4/configure.developer b/source4/configure.developer new file mode 100755 index 0000000000..0409a75061 --- /dev/null +++ b/source4/configure.developer @@ -0,0 +1,2 @@ +#!/bin/sh +`dirname $0`/configure --enable-developer $* diff --git a/source4/configure.in b/source4/configure.in new file mode 100644 index 0000000000..16b5bb5f77 --- /dev/null +++ b/source4/configure.in @@ -0,0 +1,3455 @@ +dnl -*- mode: m4-mode -*- +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) + +AC_ARG_WITH(fhs, +[ --with-fhs Use FHS-compliant paths (default=no)], + configdir="${sysconfdir}/samba" + lockdir="\${VARDIR}/cache/samba" + piddir="\${VARDIR}/run/samba" + logfilebase="\${VARDIR}/log/samba" + privatedir="\${CONFIGDIR}/private" + libdir="\${prefix}/lib/samba" + swatdir="\${DATADIR}/samba/swat", + configdir="\${LIBDIR}" + logfilebase="\${VARDIR}" + lockdir="\${VARDIR}/locks" + piddir="\${VARDIR}/locks" + privatedir="\${prefix}/private" + swatdir="\${prefix}/swat") + +################################################# +# 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]) + +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(PICFLAG) +AC_SUBST(PICSUFFIX) +AC_SUBST(POBAD_CC) +AC_SUBST(SHLIBEXT) +AC_SUBST(INSTALLCLIENTCMD_SH) +AC_SUBST(INSTALLCLIENTCMD_A) +AC_SUBST(LIBSMBCLIENT_SHARED) +AC_SUBST(LIBSMBCLIENT) +AC_SUBST(PRINTLIBS) +AC_SUBST(AUTHLIBS) +AC_SUBST(ACLLIBS) +AC_SUBST(SHLIB_PROGS) +AC_SUBST(SMBWRAPPER) +AC_SUBST(EXTRA_BIN_PROGS) +AC_SUBST(EXTRA_SBIN_PROGS) +AC_SUBST(EXTRA_ALL_TARGETS) +dnl For the DYNAMIC RPC stuff +dnl The complicated _YES and _NO stuff allows us to avoid a dependency +dnl on GNU Make. +AC_SUBST(LSA_DYNAMIC_YES) +AC_SUBST(LSA_DYNAMIC_NO) +LSA_DYNAMIC_YES="#" +LSA_DYNAMIC_NO= +AC_SUBST(NETLOG_DYNAMIC_YES) +AC_SUBST(NETLOG_DYNAMIC_NO) +NETLOG_DYNAMIC_YES="#" +NETLOG_DYNAMIC_NO= +AC_SUBST(SAMR_DYNAMIC_YES) +AC_SUBST(SAMR_DYNAMIC_NO) +SAMR_DYNAMIC_YES="#" +SAMR_DYNAMIC_NO= +AC_SUBST(SVC_DYNAMIC_YES) +AC_SUBST(SVC_DYNAMIC_NO) +SVC_DYNAMIC_YES="#" +SVC_DYNAMIC_NO= +AC_SUBST(WKS_DYNAMIC_YES) +AC_SUBST(WKS_DYNAMIC_NO) +WKS_DYNAMIC_YES="#" +WKS_DYNAMIC_NO= +AC_SUBST(REG_DYNAMIC_YES) +AC_SUBST(REG_DYNAMIC_NO) +REG_DYNAMIC_YES="#" +REG_DYNAMIC_NO= +AC_SUBST(SPOOLSS_DYNAMIC_YES) +AC_SUBST(SPOOLSS_DYNAMIC_NO) +SPOOLSS_DYNAMIC_YES="#" +SPOOLSS_DYNAMIC_NO= +AC_SUBST(DFS_DYNAMIC_YES) +AC_SUBST(DFS_DYNAMIC_NO) +DFS_DYNAMIC_YES="#" +DFS_DYNAMIC_NO= + +AC_ARG_ENABLE(debug, +[ --enable-debug Turn on compiler debugging information (default=no)], + [if eval "test x$enable_debug = xyes"; then + CFLAGS="${CFLAGS} -gstabs" + fi]) + +AC_ARG_ENABLE(developer, [ --enable-developer Turn on developer warnings and debugging (default=no)], + [if eval "test x$enable_developer = xyes"; then + 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 + CFLAGS="${CFLAGS} -gstabs -Wall -Wshadow -Wpointer-arith -Wcast-qual -Wcast-align -Wwrite-strings -DDEBUG_PASSWORD -DDEVELOPER" + fi]) + +# compile with optimization and without debugging by default +if test "x$CFLAGS" = x; then + CFLAGS = "-O"; +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 + +AC_ARG_ENABLE(dynrpc, [ --enable-dynrpc Enable dynamic RPC modules [default=no]]) + +if test x$enable_dynrpc = xyes +then + enable_dynrpc=lsa,samr,reg,wks,netlog,dfs +fi + +if test x$enable_dynrpc != xno +then + for i in `echo $enable_dynrpc | sed -e's/,/ /g'` + do case $i in lsa) + LSA_DYNAMIC_YES= + LSA_DYNAMIC_NO="#" + AC_DEFINE(RPC_LSA_DYNAMIC, 1, + [Define to make the LSA pipe dynamic]) + ;; samr) + SAMR_DYNAMIC_YES= + SAMR_DYNAMIC_NO="#" + AC_DEFINE(RPC_SAMR_DYNAMIC, 1, + [Define to make the SAMR pipe dynamic]) + ;; svc) + SVC_DYNAMIC_YES= + SVC_DYNAMIC_NO="#" + AC_DEFINE(RPC_SVC_DYNAMIC, 1, + [Define to make the SRVSVC pipe dynamic]) + ;; wks) + WKS_DYNAMIC_YES= + WKS_DYNAMIC_NO="#" + AC_DEFINE(RPC_WKS_DYNAMIC, 1, + [Define to make the WKSSVC pipe dynamic]) + ;; netlog) + NETLOG_DYNAMIC_YES= + NETLOG_DYNAMIC_NO="#" + AC_DEFINE(RPC_NETLOG_DYNAMIC, 1, + [Define to make the NETLOGON pipe dynamic]) + ;; reg) + REG_DYNAMIC_YES= + REG_DYNAMIC_NO="#" + AC_DEFINE(RPC_REG_DYNAMIC, 1, + [Define to make the WINREG pipe dynamic]) + ;; spoolss) + SPOOLSS_DYNAMIC_YES= + SPOOLSS_DYNAMIC_NO="#" + AC_DEFINE(RPC_SPOOLSS_DYNAMIC, 1, + [Define to make the SPOOLSS pipe dynamic]) + ;; dfs) + DFS_DYNAMIC_YES= + DFS_DYNAMIC_NO="#" + AC_DEFINE(RPC_DFS_DYNAMIC, 1, + [Define to make the NETDFS pipe dynamic]) + ;; esac + done +fi + +dnl Checks for programs. +AC_PROG_CC +AC_PROG_INSTALL +AC_PROG_AWK +AC_PATH_PROG(PERL, perl) + +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 volatile (it should, being ANSI). +AC_CACHE_CHECK([that the C compiler understands volatile],samba_cv_volatile, [ + AC_TRY_COMPILE([#include ],[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 + + +AC_CANONICAL_SYSTEM + +dnl Add #include for broken IRIX header files + case "$host_os" in + *irix6*) AC_ADD_INCLUDE() + ;; +esac + +AC_VALIDATE_CACHE_SYSTEM_TYPE + +DYNEXP= + +# +# 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*) + 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 + ;; +# +# 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 +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 +#include +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 = 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]) + ;; + + *hurd*) + AC_MSG_CHECKING([for LFS support]) + old_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="-D_LARGEFILE64_SOURCE -D_GNU_SOURCE $CPPFLAGS" + AC_TRY_RUN([ +#include +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(compat.h 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 ldap.h lber.h dlfcn.h) +AC_CHECK_HEADERS(sys/syslog.h syslog.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 ],[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 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/cdefs.h glob.h) + +# For experimental utmp support (lastlog on some BSD-like systems) +AC_CHECK_HEADERS(utmp.h utmpx.h lastlog.h) + +# For quotas on Veritas VxFS filesystems +AC_CHECK_HEADERS(sys/fs/vx_quota.h) + +# For quotas on Linux XFS filesystems +AC_CHECK_HEADERS(linux/xqm.h) +AC_CHECK_HEADERS(xfs/xqm.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`" + PRINTLIBS="$PRINTLIBS `$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 ], +[ + 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 for unix domain sockets +AC_CACHE_CHECK([for unix domain sockets],samba_cv_unixsocket, [ + AC_TRY_COMPILE([ +#include +#include +#include +#include +#include ], +[ + 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 +#if STDC_HEADERS +#include +#include +#endif +#include ],[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 +#if STDC_HEADERS +#include +#include +#endif +#include ],[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 ]) +AC_HAVE_DECL(setresuid, [#include ]) +AC_HAVE_DECL(setresgid, [#include ]) +AC_HAVE_DECL(asprintf, [#include ]) +AC_HAVE_DECL(vasprintf, [#include ]) +AC_HAVE_DECL(vsnprintf, [#include ]) +AC_HAVE_DECL(snprintf, [#include ]) + +# 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 +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 +#include +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 getgrouplist timegm) +# setbuffer, shmget, shm_open are needed for smbtorture +AC_CHECK_FUNCS(setbuffer shmget shm_open) + +# 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) + +# +# stat64 family may need on some systems, notably ReliantUNIX +# + +if test x$ac_cv_func_stat64 = xno ; then + AC_MSG_CHECKING([for stat64 in ]) + AC_TRY_LINK([ +#if defined(HAVE_UNISTD_H) +#include +#endif +#include +], [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 ]) + AC_TRY_LINK([ +#if defined(HAVE_UNISTD_H) +#include +#endif +#include +], [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 ]) + AC_TRY_LINK([ +#if defined(HAVE_UNISTD_H) +#include +#endif +#include +], [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) + +# 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}" +PICFLAG="" +PICSUFFIX="po" +POBAD_CC="#" +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" + PICFLAG="-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 + PICFLAG="-fPIC" + if test "${ac_cv_prog_gnu_ld}" = "yes"; then + DYNEXP="-Wl,-E" + fi + else + PICFLAG="-KPIC" + ## ${CFLAGS} added for building 64-bit shared + ## libs using Sun's Compiler + LDSHFLAGS="-G \${CFLAGS}" + POBAD_CC="" + PICSUFFIX="po.o" + fi + AC_DEFINE(STAT_ST_BLOCKSIZE,512,[The size of a block]) + ;; + *sunos*) AC_DEFINE(SUNOS4,1,[Whether the host os is sunos4]) + BLDSHARED="true" + LDSHFLAGS="-G" + SONAMEFLAG="-Wl,-h," + PICFLAG="-KPIC" # Is this correct for SunOS + AC_DEFINE(STAT_ST_BLOCKSIZE,512) + ;; + *netbsd* | *freebsd*) BLDSHARED="true" + LDSHFLAGS="-shared" + DYNEXP="-Wl,--export-dynamic" + SONAMEFLAG="-Wl,-soname," + PICFLAG="-fPIC -DPIC" + AC_DEFINE(STAT_ST_BLOCKSIZE,512,[The size of a block]) + ;; + *openbsd*) BLDSHARED="true" + LDSHFLAGS="-shared" + DYNEXP="-Wl,-Bdynamic" + SONAMEFLAG="-Wl,-soname," + PICFLAG="-fPIC" + AC_DEFINE(STAT_ST_BLOCKSIZE,512,[The size of a block]) + ;; + *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 + ATTEMPT_WRAP32_BUILD=yes + BLDSHARED="true" + LDSHFLAGS="-set_version sgi1.0 -shared" + SONAMEFLAG="-soname " + SHLD="\${LD}" + if test "${GCC}" = "yes"; then + PICFLAG="-fPIC" + else + PICFLAG="-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" + DYNEXP="-Wl,-brtl,-bexpall" + PICFLAG="-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 " + PICFLAG="+z" + fi + DYNEXP="-Wl,-E" + AC_DEFINE(STAT_ST_BLOCKSIZE,8192,[The size of a block]) + ;; + *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," + PICFLAG="-fPIC" + AC_DEFINE(STAT_ST_BLOCKSIZE,512) + ;; + *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," + PICFLAG="-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="" + ;; + *) + 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 + if test "$PICSUFFIX" = "po"; then + $CC $CPPFLAGS $CFLAGS $PICFLAG -c -o shlib.po ${srcdir-.}/tests/shlib.c && + $CC $CPPFLAGS $CFLAGS `eval echo $LDSHFLAGS` -o shlib.so shlib.po && + ac_cv_shlib_works=yes + else + $CC $CPPFLAGS $CFLAGS $PICFLAG -c -o shlib.$PICSUFFIX ${srcdir-.}/tests/shlib.c && + mv shlib.$PICSUFFIX shlib.po && + $CC $CPPFLAGS $CFLAGS `eval echo $LDSHFLAGS` -o shlib.so shlib.po && + ac_cv_shlib_works=yes + fi + rm -f shlib.so shlib.po +]) +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 +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 ],[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 +#include +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 +#endif +#include +#include +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 +#include +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 +#endif +#include +#include +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 +#endif +#include +#include +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 +#endif +#include +#include ], +[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 +#endif +#include +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 +#endif +#include +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 +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 +#include +#include ], +[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 +#include +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 ], [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 +#include +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 +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 + +AC_CACHE_CHECK([for C99 vsnprintf],samba_cv_HAVE_C99_VSNPRINTF,[ +AC_TRY_RUN([ +#include +#include +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 +#include +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 +#include ], +[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 + +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 +#include ], +[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 +#include ], +[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 +#include ], +[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 +#include ], +[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 +#include ], +[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 +#include ], +[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 +#include ], +[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 +#include ], +[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 +#include ], +[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 +#include ], +[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 +#include ], + [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 +#include ], +[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 + + +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(I won't take no for an answer) + else + if test "$withval" != "yes" ; then + LOOK_DIRS="$withval $LOOK_DIRS" + fi + fi +]) + +ICONV_FOUND="no" +for i in $LOOK_DIRS ; do + save_LIBS=$LIBS + save_LDFLAGS=$LDFLAGS + save_CPPFLAGS=$CPPFLAGS + CPPFLAGS="-I$i/include" + LDFLAGS="-L$i/lib" + LIBS= + export LDFLAGS LIBS CPPFLAGS +dnl Try to find iconv(3) + jm_ICONV($i) + + CPPFLAGS=$save_CPPFLAGS + if test -n "$ICONV_FOUND" ; then + LDFLAGS=$save_LDFLAGS + LIB_ADD_DIR(LDFLAGS, "$i/lib") + CFLAGS_ADD_DIR(CPPFLAGS, "$i/include") + LIBS="$save_LIBS $LIBS" + ICONV_LOCATION=$i + export LDFLAGS LIBS CPPFLAGS + break + else + LDFLAGS=$save_LDFLAGS + LIBS=$save_LIBS + export LDFLAGS LIBS CPPFLAGS + fi +done + +############ +# check for iconv in libc +AC_CACHE_CHECK([for working iconv],samba_cv_HAVE_NATIVE_ICONV,[ +AC_TRY_RUN([ +#include +main() { + iconv_t cd = iconv_open("ASCII", "UCS-2LE"); + if (cd == 0 || cd == (iconv_t)-1) return -1; + return 0; +} +], +samba_cv_HAVE_NATIVE_ICONV=yes,samba_cv_HAVE_NATIVE_ICONV=no,samba_cv_HAVE_NATIVE_ICONV=cross)]) +if test x"$samba_cv_HAVE_NATIVE_ICONV" = x"yes"; then + AC_DEFINE(HAVE_NATIVE_ICONV,1,[Whether to use native iconv]) +fi + +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!]) +fi + + +AC_CACHE_CHECK([for Linux kernel oplocks],samba_cv_HAVE_KERNEL_OPLOCKS_LINUX,[ +AC_TRY_RUN([ +#include +#include +#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 +#include +#include +#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 +#include +#include +#include +#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 +#include ], +[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 +#include +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 +#if defined(HAVE_RPC_RPC_H) +#include +#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 +#if defined(HAVE_RPC_RPC_H) +#include +#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 +#if defined(HAVE_RPC_RPC_H) +#include +#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 +#if defined(HAVE_RPC_RPC_H) +#include +#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 and due to AUTH_ERROR being defined +dnl as a #define in and as part of an enum +dnl in . +dnl + +AC_CACHE_CHECK([for conflicting AUTH_ERROR define in rpc/rpc.h],samba_cv_HAVE_RPC_AUTH_ERROR_CONFLICT,[ +AC_TRY_COMPILE([#include +#ifdef HAVE_SYS_SECURITY_H +#include +#include +#endif /* HAVE_SYS_SECURITY_H */ +#if defined(HAVE_RPC_RPC_H) +#include +#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_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 +#include +#include +#ifdef HAVE_ARPA_INET_H +#include +#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 +#include +#include +#include +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 +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 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 +#endif +#include +#include + +#ifdef HAVE_FCNTL_H +#include +#endif + +#ifdef HAVE_SYS_FCNTL_H +#include +#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 +#include +#include ], +[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 +#include +#include ], +[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 +#endif +#ifdef HAVE_SYS_CAPABILITY_H +#include +#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 +#if defined(HAVE_RPCSVC_NIS_H) +#include +#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" + + if test x$ATTEMPT_WRAP32_BUILD = x; then + WRAP32="" + else + WRAP32=bin/smbwrapper.32.$SHLIBEXT + fi + +# Conditions under which smbwrapper should not be built. + + if test x$PICFLAG = x; then + echo No support for PIC code - disabling smbwrapper and smbsh + WRAPPROG="" + WRAP="" + WRAP32="" + elif test x$ac_cv_func_syscall = xno; then + AC_MSG_RESULT([No syscall() -- disabling smbwrapper and smbsh]) + WRAPPROG="" + WRAP="" + WRAP32="" + fi + EXTRA_ALL_TARGETS="$EXTRA_ALL_TARGETS $WRAPPROG $WRAP $WRAP32" + SMBWRAPPER="$WRAPPROG $WRAP $WRAP32" + ;; + *) + AC_MSG_RESULT(no) + ;; + esac ], + AC_MSG_RESULT(no) +) + +################################################# +# check for AFS clear-text auth support +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) + AC_MSG_RESULT(yes) + AC_DEFINE(WITH_AFS,1,[Whether to include AFS clear-text auth support]) + ;; + *) + AC_MSG_RESULT(no) + ;; + esac ], + AC_MSG_RESULT(no) +) + +################################################# +# check for pthread support +AC_MSG_CHECKING(whether to use pthreads) +AC_ARG_WITH(pthreads, +[ --with-pthreads Include pthreads (default=no) ], +[ case "$withval" in + yes) + AC_MSG_RESULT(yes) + AC_DEFINE(WITH_PTHREADS,1,[Whether to use pthreads]) + SMBD_EXTRA_OBJS="smbd/process_thread.o" + SMBD_EXTRA_LIBS="-lpthread" + ;; + *) + AC_MSG_RESULT(no) + ;; + esac ], + AC_MSG_RESULT(no) +) + +AC_SUBST(SMBD_EXTRA_OBJS) +AC_SUBST(SMBD_EXTRA_LIBS) + + +################################################# +# check for STFS NTVFS backend +STFS_ENABLED=# +AC_MSG_CHECKING(whether to use STFS NTVFS backend) +AC_ARG_WITH(stfs, +[ --with-stfs Include STFS NTVFS backend (default=no) ], +[ case "$withval" in + yes) + AC_MSG_RESULT(yes) + AC_DEFINE(WITH_NTVFS_STFS,1,[Whether to include STFS NTVFS backend]) + STFS_ENABLED= + ;; + *) + AC_MSG_RESULT(no) + ;; + esac ], + AC_MSG_RESULT(no) +) + +AC_SUBST(STFS_ENABLED) + + +################################################# +# 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) +) + +################################################# +# active directory support + +with_ads_support=yes +AC_MSG_CHECKING([whether to use Active Directory]) + +AC_ARG_WITH(ads, +[ --with-ads Active Directory support (default yes)], +[ case "$withval" in + no) + with_ads_support=no + ;; + esac ]) + +if test x"$with_ads_support" = x"yes"; then + AC_DEFINE(WITH_ADS,1,[Whether to include Active Directory support]) +fi + +AC_MSG_RESULT($with_ads_support) + +FOUND_KRB5=no +if test x"$with_ads_support" = x"yes"; then + + ################################################# + # 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 + LIBS="$LIBS `$KRB5_CONFIG --libs`" + CFLAGS="$CFLAGS `$KRB5_CONFIG --cflags`" + CPPFLAGS="$CPPFLAGS `$KRB5_CONFIG --cflags`" + 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) + ;; + *) + AC_MSG_RESULT(yes) + LIBS="$LIBS -lkrb5" + CFLAGS="$CFLAGS -I$withval/include" + CPPFLAGS="$CPPFLAGS -I$withval/include" + LDFLAGS="$LDFLAGS -L$withval/lib" + FOUND_KRB5=yes + ;; + esac ], + AC_MSG_RESULT(no) + ) + fi + +if test x$FOUND_KRB5 = x"no"; then +################################################# +# see if this box has the SuSE location for the heimdal kerberos 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 + LIBS="$LIBS -lkrb5" + CFLAGS="$CFLAGS -I/usr/include/heimdal" + CPPFLAGS="$CPPFLAGS -I/usr/include/heimdal" + LDFLAGS="$LDFLAGS -L/usr/lib/heimdal/lib" + AC_MSG_RESULT(yes) + else + LIBS="$LIBS -lkrb5" + CFLAGS="$CFLAGS -I/usr/include/heimdal" + CPPFLAGS="$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 + LIBS="$LIBS -lkrb5" + LDFLAGS="$LDFLAGS -L/usr/kerberos/lib" + CFLAGS="$CFLAGS -I/usr/kerberos/include" + CPPFLAGS="$CPPFLAGS -I/usr/kerberos/include" + AC_MSG_RESULT(yes) +else + AC_MSG_RESULT(no) +fi +fi + + # 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) + + # 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(com_err, _et_list, [LIBS="$LIBS -lcom_err"]) + AC_CHECK_LIB(k5crypto, krb5_encrypt_data, [LIBS="$LIBS -lk5crypto"]) + # Heimdal checks. + AC_CHECK_LIB(crypto, des_set_key, [LIBS="$LIBS -lcrypto"]) + AC_CHECK_LIB(asn1, copy_Authenticator, [LIBS="$LIBS -lasn1 -lroken"]) + # Heimdal checks. On static Heimdal gssapi must be linked before krb5. + AC_CHECK_LIB(gssapi, gss_display_status, [LIBS="$LIBS -lgssapi -lkrb5 -lasn1"; + AC_DEFINE(HAVE_GSSAPI,1,[Whether GSSAPI is available])]) + + AC_CHECK_LIB(krb5, krb5_set_real_time, [AC_DEFINE(HAVE_KRB5_SET_REAL_TIME,1,[Whether krb5_set_real_time is available])]) + AC_CHECK_LIB(krb5, krb5_set_default_in_tkt_etypes, [AC_DEFINE(HAVE_KRB5_SET_DEFAULT_IN_TKT_ETYPES,1,[Whether krb5_set_default_in_tkt_etypes, is available])]) + AC_CHECK_LIB(krb5, krb5_set_default_tgs_ktypes, [AC_DEFINE(HAVE_KRB5_SET_DEFAULT_TGS_KTYPES,1,[Whether krb5_set_default_tgs_ktypes is available])]) + + AC_CHECK_LIB(krb5, krb5_principal2salt, [AC_DEFINE(HAVE_KRB5_PRINCIPAL2SALT,1,[Whether krb5_principal2salt is available])]) + AC_CHECK_LIB(krb5, krb5_use_enctype, [AC_DEFINE(HAVE_KRB5_USE_ENCTYPE,1,[Whether krb5_use_enctype is available])]) + AC_CHECK_LIB(krb5, krb5_string_to_key, [AC_DEFINE(HAVE_KRB5_STRING_TO_KEY,1,[Whether krb5_string_to_key is available])]) + AC_CHECK_LIB(krb5, krb5_get_pw_salt, [AC_DEFINE(HAVE_KRB5_GET_PW_SALT,1,[Whether krb5_get_pw_salt is available])]) + AC_CHECK_LIB(krb5, krb5_string_to_key_salt, [AC_DEFINE(HAVE_KRB5_STRING_TO_KEY_SALT,1,[Whether krb5_string_to_key_salt is available])]) + AC_CHECK_LIB(krb5, krb5_auth_con_setkey, [AC_DEFINE(HAVE_KRB5_AUTH_CON_SETKEY,1,[Whether krb5_auth_con_setkey is available])]) + AC_CHECK_LIB(krb5, krb5_auth_con_setuseruserkey, [AC_DEFINE(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY,1,[Whether krb5_auth_con_setuseruserkey is available])]) + AC_CHECK_LIB(krb5, krb5_locate_kdc, [AC_DEFINE(HAVE_KRB5_LOCATE_KDC,1,[Whether krb5_locate_kdc is available])]) + AC_CHECK_LIB(krb5, krb5_get_permitted_enctypes, [AC_DEFINE(HAVE_KRB5_GET_PERMITTED_ENCTYPES,1,[Whether krb5_get_permitted_enctypes is available])]) + AC_CHECK_LIB(krb5, krb5_get_default_in_tkt_etypes, [AC_DEFINE(HAVE_KRB5_GET_DEFAULT_IN_TKT_ETYPES,1,[Whether krb5_get_default_in_tkt_etypes is available])]) + AC_CHECK_LIB(krb5, krb5_free_ktypes, [AC_DEFINE(HAVE_KRB5_FREE_KTYPES,1,[Whether krb5_free_ktypes is available])]) + +AC_CACHE_CHECK([for addrtype in krb5_address],samba_cv_HAVE_ADDRTYPE_IN_KRB5_ADDRESS,[ +AC_TRY_COMPILE([#include ], +[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_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_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_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_enctype enctype; enctype = ENCTYPE_ARCFOUR_HMAC_MD5;], +samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC_MD5=yes,samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC_MD5=no)]) +if test x"$samba_cv_HAVE_ENCTYPE_ARCFOUR_HMAC_MD5" = x"yes"; then + AC_DEFINE(HAVE_ENCTYPE_ARCFOUR_HMAC_MD5,1,[Whether the ENCTYPE_ARCFOUR_HMAC_MD5 key type is available]) +fi + + ######################################################## + # now see if we can find the krb5 libs in standard paths + # or as specified above + AC_CHECK_LIB(krb5, krb5_mk_req_extended, [LIBS="$LIBS -lkrb5"; + AC_DEFINE(HAVE_KRB5,1,[Whether KRB5 is available])]) + + ######################################################## + # now see if we can find the gssapi libs in standard paths + AC_CHECK_LIB(gssapi_krb5, gss_display_status, [LIBS="$LIBS -lgssapi_krb5"; + AC_DEFINE(HAVE_GSSAPI,1,[Whether GSSAPI is available])]) + +fi + +######################################################## +# Compile with LDAP support? + +with_ldap_support=yes +AC_MSG_CHECKING([whether to use LDAP]) + +AC_ARG_WITH(ldap, +[ --with-ldap LDAP support (default yes)], +[ case "$withval" in + no) + with_ldap_support=no + ;; + esac ]) + +AC_MSG_RESULT($with_ldap_support) + +if test x"$with_ldap_support" = x"yes"; then + + ################################################################## + # we might need the lber lib on some systems. To avoid link errors + # this test must be before the libldap test + AC_CHECK_LIB(lber, ber_scanf, [LIBS="$LIBS -llber"]) + + ######################################################## + # now see if we can find the ldap libs in standard paths + if test x$have_ldap != xyes; then + AC_CHECK_LIB(ldap, ldap_domain2hostlist, [LIBS="$LIBS -lldap"; + AC_DEFINE(HAVE_LDAP,1,[Whether ldap is available])]) + + ######################################################## + # If we have LDAP, does it's rebind procedure take 2 or 3 arguments? + # Check found in pam_ldap 145. + AC_CHECK_FUNCS(ldap_set_rebind_proc) + AC_CACHE_CHECK(whether ldap_set_rebind_proc takes 3 arguments, pam_ldap_cv_ldap_set_rebind_proc, [ + AC_TRY_COMPILE([ + #include + #include ], [ldap_set_rebind_proc(0, 0, 0);], [pam_ldap_cv_ldap_set_rebind_proc=3], [pam_ldap_cv_ldap_set_rebind_proc=2]) ]) + AC_DEFINE_UNQUOTED(LDAP_SET_REBIND_PROC_ARGS, $pam_ldap_cv_ldap_set_rebind_proc, [Number of arguments to ldap_set_rebind_proc]) + fi +fi + +######################################################## +# Compile with MySQL support? +AM_PATH_MYSQL([0.11.0],[MODULE_MYSQL=bin/mysql.so],[MODULE_MYSQL=]) +CFLAGS="$CFLAGS $MYSQL_CFLAGS" +AC_SUBST(MODULE_MYSQL) + +######################################################## +# Compile with XML support? +AM_PATH_XML2([2.0.0],[MODULE_XML=bin/xml.so],[MODULE_XML=]) +CFLAGS="$CFLAGS $XML_CFLAGS" +AC_SUBST(MODULE_XML) + +################################################# +# 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) + AC_DEFINE(WITH_PAM,1,[Whether to include PAM support]) + AUTHLIBS="$AUTHLIBS -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 a PAM module to allow other applications to use our smbpasswd file (default=no)], +[ case "$withval" in + yes) + AC_MSG_RESULT(yes) + +# Conditions under which pam_smbpass should not be built. + + if test x$PICFLAG = x; then + AC_MSG_RESULT([No support for PIC code - disabling pam_smbpass]) + elif test x$ac_cv_lib_pam_pam_get_data = xno; then + AC_MSG_RESULT([No libpam found -- disabling pam_smbpass]) + else + SHLIB_PROGS="$SHLIB_PROGS bin/pam_smbpass.so" + 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" || AUTHLIBS="-lcrypt $AUTHLIBS" + 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="$AUTHLIBS $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 + +# New experimental SAM system + +AC_MSG_CHECKING([whether to build the new (experimental) SAM database]) +AC_ARG_WITH(sam, +[ --with-sam Build new (experimental) SAM database (default=no)], +[ case "$withval" in + yes) + AC_MSG_RESULT(yes) + AC_DEFINE(WITH_SAM,1,[Whether to build the new (experimental) SAM database]) + ;; + *) + AC_MSG_RESULT(no) + ;; + esac ], + AC_MSG_RESULT(no) +) + + +######################################################################################## +## +## 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 compatibel LDAP SAM configuration]) + ;; + *) + AC_MSG_RESULT(no) + ;; + esac ], + AC_MSG_RESULT(no) +) + +################################################# +# check for a TDB password database +AC_MSG_CHECKING(whether to use TDB SAM database) +AC_ARG_WITH(tdbsam, +[ --with-tdbsam Include experimental TDB SAM support (default=no)], +[ case "$withval" in + yes) + AC_MSG_RESULT(yes) + AC_DEFINE(WITH_TDB_SAM,1,[Whether to include experimental TDB SAM support]) + ;; + *) + AC_MSG_RESULT(no) + ;; + esac ], + AC_MSG_RESULT(no) +) + +################################################# +# check for a NISPLUS password database +AC_MSG_CHECKING(whether to use NISPLUS SAM database) +AC_ARG_WITH(nisplussam, +[ --with-nisplussam Include NISPLUS SAM support (default=no)], +[ case "$withval" in + yes) + AC_MSG_RESULT(yes) + AC_DEFINE(WITH_NISPLUS_SAM,1,[Whether to include nisplus SAM support]) + ;; + *) + 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 experimental disk-quotas support +QUOTAOBJS=smbd/noquotas.o + +AC_MSG_CHECKING(whether to support disk-quotas) +AC_ARG_WITH(quotas, +[ --with-quotas Include experimental disk-quota support (default=no)], +[ case "$withval" in + yes) + AC_MSG_RESULT(yes) + case "$host_os" in + *linux*) + # Check for kernel 2.4.x quota braindamage... + AC_CACHE_CHECK([for linux 2.4.x quota braindamage..],samba_cv_linux_2_4_quota_braindamage, [ + AC_TRY_COMPILE([#include +#include +#include +#include +#include +#include ],[struct mem_dqblk D;], + samba_cv_linux_2_4_quota_braindamage=yes,samba_cv_linux_2_4_quota_braindamage=no)]) +if test x"$samba_cv_linux_2_4_quota_braindamage" = x"yes"; then + AC_DEFINE(LINUX_QUOTAS_2,1,[linux 2.4.x quota braindamage]) +else + AC_DEFINE(LINUX_QUOTAS_1,1,[linux quotas]) +fi + ;; + *) + ;; + esac + QUOTAOBJS=smbd/quotas.o + AC_DEFINE(WITH_QUOTAS,1,[Whether to include experimental quota support]) + ;; + *) + AC_MSG_RESULT(no) + ;; + esac ], + AC_MSG_RESULT(no) +) +AC_SUBST(QUOTAOBJS) + +################################################# +# check for experimental utmp accounting + +AC_MSG_CHECKING(whether to support utmp accounting) +AC_ARG_WITH(utmp, +[ --with-utmp Include experimental utmp accounting (default=no)], +[ case "$withval" in + yes) + AC_MSG_RESULT(yes) + AC_DEFINE(WITH_UTMP,1,[Whether to include experimental utmp accounting]) + ;; + *) + AC_MSG_RESULT(no) + ;; + esac ], + AC_MSG_RESULT(no) +) + +################################################# +# 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=: +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)" + 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 + ;; + 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] +) + + +################################################# +# 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 +#endif +#include +#include + 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 +#include ], + [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 +#include +#include + 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 +#endif +#ifdef HAVE_SYS_MOUNT_H +#include +#endif +#ifdef HAVE_SYS_VFS_H +#include +#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 +#include + 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 +#ifdef HAVE_SYS_PARAM_H +#include +#endif +#ifdef HAVE_SYS_MOUNT_H +#include +#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 +#ifdef HAVE_SYS_PARAM_H +#include +#endif +#ifdef HAVE_SYS_MOUNT_H +#include +#endif +#ifdef HAVE_SYS_FS_TYPES_H +#include +#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 +#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]) + +################################################# +# 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]) + ACLLIBS="$ACLLIBS -lpacl" + ;; + *) + AC_CHECK_LIB(acl,acl_get_file,[ACLLIBS="$ACLLIBS -lacl"]) + AC_CACHE_CHECK([for ACL support],samba_cv_HAVE_POSIX_ACLS,[ + acl_LIBS=$LIBS + LIBS="$LIBS -lacl" + AC_TRY_LINK([#include +#include ], +[ 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 +#include ], +[ 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 ], +[\ +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 ], +[\ +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 ], +[\ +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 +#include +#include +#include ], +[\ + 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 +#include ], +[\ + 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 +#include ], +[\ + 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 ], +[\ + 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 ], +[\ + 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 + +case "$host_os" in + *linux*|*irix*) + HAVE_WINBIND=yes + ;; + *solaris*) + HAVE_WINBIND=yes + WINBIND_NSS_EXTRA_OBJS="nsswitch/winbind_nss_solaris.o" + WINBIND_NSS_EXTRA_LIBS="-lsocket" + ;; + *hpux11*) + HAVE_WINBIND=yes + WINBIND_NSS_EXTRA_OBJS="nsswitch/winbind_nss_solaris.o" + ;; + *) + HAVE_WINBIND=no + winbind_no_reason=", unsupported on $host_os" + ;; +esac + +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 + case "$host_os" in + *irix*) + SHLIB_PROGS="$SHLIB_PROGS nsswitch/libns_winbind.so" + ;; + *) + SHLIB_PROGS="$SHLIB_PROGS nsswitch/libnss_winbind.so" + ;; + esac + if test x"$with_pam" = x"yes"; then + SHLIB_PROGS="$SHLIB_PROGS nsswitch/pam_winbind.so" + 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. Unfortunately autoconf < 2.50 +# doesn't have the AC_CHECK_MEMBER macro which would be handy for checking +# this. + +#AC_CHECK_MEMBER(struct passwd.pw_comment, +# AC_DEFINE(HAVE_PASSWD_PW_COMMENT, 1, [Defined if struct passwd has pw_comment field]), +# [#include ]) + +AC_CACHE_CHECK([whether struct passwd has pw_comment],samba_cv_passwd_pw_comment, [ + AC_TRY_COMPILE([#include ],[struct passwd p; p.pw_comment;], + samba_cv_passwd_pw_comment=yes,samba_cv_passwd_pw_comment=no)]) +if test x"$samba_cv_passwd_pw_comment" = x"yes"; then + AC_DEFINE(HAVE_PASSWD_PW_COMMENT,1,[Whether struct passwd has pw_comment]) +fi + +#AC_CHECK_MEMBER(struct passwd.pw_age, +# AC_DEFINE(HAVE_PASSWD_PW_AGE, 1, [Defined if struct passwd has pw_age field]), +# [#include ]) + +AC_CACHE_CHECK([whether struct passwd has pw_age],samba_cv_passwd_pw_age, [ + AC_TRY_COMPILE([#include ],[struct passwd p; p.pw_age;], + samba_cv_passwd_pw_age=yes,samba_cv_passwd_pw_age=no)]) +if test x"$samba_cv_passwd_pw_age" = x"yes"; then + AC_DEFINE(HAVE_PASSWD_PW_AGE,1,[Whether struct passwd has pw_age]) +fi + +################################################# +# 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)' + FLAGS1="-I$srcdir/popt" +else + AC_MSG_RESULT(no) + LIBS="$LIBS -lpopt" +fi +AC_SUBST(BUILD_POPT) +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) + +################################################# +# do extra things if we are running insure + +if test "${ac_cv_prog_CC}" = "insure"; then + CPPFLAGS="$CPPFLAGS -D__INSURE__" +fi + +################################################# +# 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) + +################################################# +# 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/source4/configure.nodebug.developer b/source4/configure.nodebug.developer new file mode 100755 index 0000000000..65e21b4bdf --- /dev/null +++ b/source4/configure.nodebug.developer @@ -0,0 +1,3 @@ +#!/bin/sh +CFLAGS="-Wall -Wshadow -Wstrict-prototypes -Wpointer-arith -Wcast-qual -Wcast-align -DDEBUG_PASSWORD"; export CFLAGS +./configure $* diff --git a/source4/configure.tridge.opt b/source4/configure.tridge.opt new file mode 100755 index 0000000000..5ed55b3b96 --- /dev/null +++ b/source4/configure.tridge.opt @@ -0,0 +1,3 @@ +#!/bin/sh +export CFLAGS="-O2 -Wall" +`dirname $0`/configure $* --prefix=/home/tridge/samba/samba4/prefix diff --git a/source4/dynconfig.c b/source4/dynconfig.c new file mode 100644 index 0000000000..42e8dff0ca --- /dev/null +++ b/source4/dynconfig.c @@ -0,0 +1,72 @@ +/* + Unix SMB/CIFS implementation. + Copyright (C) 2001 by Martin Pool + Copyright (C) 2003 by Anthony Liguori + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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. **/ +const char *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/source4/groupdb/mapping.c b/source4/groupdb/mapping.c new file mode 100644 index 0000000000..958d6ded49 --- /dev/null +++ b/source4/groupdb/mapping.c @@ -0,0 +1,1340 @@ +/* + * 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/" + +PRIVS privs[] = { + {SE_PRIV_NONE, "no_privs", "No privilege" }, /* this one MUST be first */ + {SE_PRIV_ADD_MACHINES, "SeMachineAccountPrivilege", "Add workstations to the domain" }, + {SE_PRIV_SEC_PRIV, "SeSecurityPrivilege", "Manage the audit logs" }, + {SE_PRIV_TAKE_OWNER, "SeTakeOwnershipPrivilege", "Take ownership of file" }, + {SE_PRIV_ADD_USERS, "SaAddUsers", "Add users to the domain - Samba" }, + {SE_PRIV_PRINT_OPERATOR, "SaPrintOp", "Add or remove printers - Samba" }, + {SE_PRIV_ALL, "SaAllPrivs", "all privileges" } +}; +/* +PRIVS privs[] = { + { 2, "SeCreateTokenPrivilege" }, + { 3, "SeAssignPrimaryTokenPrivilege" }, + { 4, "SeLockMemoryPrivilege" }, + { 5, "SeIncreaseQuotaPrivilege" }, + { 6, "SeMachineAccountPrivilege" }, + { 7, "SeTcbPrivilege" }, + { 8, "SeSecurityPrivilege" }, + { 9, "SeTakeOwnershipPrivilege" }, + { 10, "SeLoadDriverPrivilege" }, + { 11, "SeSystemProfilePrivilege" }, + { 12, "SeSystemtimePrivilege" }, + { 13, "SeProfileSingleProcessPrivilege" }, + { 14, "SeIncreaseBasePriorityPrivilege" }, + { 15, "SeCreatePagefilePrivilege" }, + { 16, "SeCreatePermanentPrivilege" }, + { 17, "SeBackupPrivilege" }, + { 18, "SeRestorePrivilege" }, + { 19, "SeShutdownPrivilege" }, + { 20, "SeDebugPrivilege" }, + { 21, "SeAuditPrivilege" }, + { 22, "SeSystemEnvironmentPrivilege" }, + { 23, "SeChangeNotifyPrivilege" }, + { 24, "SeRemoteShutdownPrivilege" }, + { 25, "SeUndockPrivilege" }, + { 26, "SeSyncAgentPrivilege" }, + { 27, "SeEnableDelegationPrivilege" }, +}; +*/ + + /* + * Those are not really privileges like the other ones. + * They are handled in a special case and called + * system privileges. + * + * SeNetworkLogonRight + * SeUnsolicitedInputPrivilege + * SeBatchLogonRight + * SeServiceLogonRight + * SeInteractiveLogonRight + * SeDenyInteractiveLogonRight + * SeDenyNetworkLogonRight + * SeDenyBatchLogonRight + * SeDenyBatchLogonRight + */ + +#if 0 +/**************************************************************************** +check if the user has the required privilege. +****************************************************************************/ +static BOOL se_priv_access_check(NT_USER_TOKEN *token, uint32 privilege) +{ + /* no token, no privilege */ + if (token==NULL) + return False; + + if ((token->privilege & privilege)==privilege) + return True; + + return False; +} +#endif + +/**************************************************************************** +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; + LUID_ATTR set; + + PRIVILEGE_SET privilege_none; + PRIVILEGE_SET privilege_all; + PRIVILEGE_SET privilege_print_op; + + init_privilege(&privilege_none); + init_privilege(&privilege_all); + init_privilege(&privilege_print_op); + + set.attr=0; + set.luid.high=0; + set.luid.low=SE_PRIV_PRINT_OPERATOR; + add_privilege(&privilege_print_op, set); + + add_all_privilege(&privilege_all); + + /* Add the Wellknown groups */ + + add_initial_entry(-1, "S-1-5-32-544", SID_NAME_ALIAS, "Administrators", "", privilege_all, PR_ACCESS_FROM_NETWORK|PR_LOG_ON_LOCALLY); + add_initial_entry(-1, "S-1-5-32-545", SID_NAME_ALIAS, "Users", "", privilege_none, PR_ACCESS_FROM_NETWORK|PR_LOG_ON_LOCALLY); + add_initial_entry(-1, "S-1-5-32-546", SID_NAME_ALIAS, "Guests", "", privilege_none, PR_ACCESS_FROM_NETWORK); + add_initial_entry(-1, "S-1-5-32-547", SID_NAME_ALIAS, "Power Users", "", privilege_none, PR_ACCESS_FROM_NETWORK|PR_LOG_ON_LOCALLY); + + add_initial_entry(-1, "S-1-5-32-548", SID_NAME_ALIAS, "Account Operators", "", privilege_none, PR_ACCESS_FROM_NETWORK|PR_LOG_ON_LOCALLY); + add_initial_entry(-1, "S-1-5-32-549", SID_NAME_ALIAS, "System Operators", "", privilege_none, PR_ACCESS_FROM_NETWORK|PR_LOG_ON_LOCALLY); + add_initial_entry(-1, "S-1-5-32-550", SID_NAME_ALIAS, "Print Operators", "", privilege_print_op, PR_ACCESS_FROM_NETWORK|PR_LOG_ON_LOCALLY); + add_initial_entry(-1, "S-1-5-32-551", SID_NAME_ALIAS, "Backup Operators", "", privilege_none, PR_ACCESS_FROM_NETWORK|PR_LOG_ON_LOCALLY); + + add_initial_entry(-1, "S-1-5-32-552", SID_NAME_ALIAS, "Replicators", "", privilege_none, PR_ACCESS_FROM_NETWORK); + + /* 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", "", privilege_all, PR_ACCESS_FROM_NETWORK|PR_LOG_ON_LOCALLY); + + 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", "", privilege_none, PR_ACCESS_FROM_NETWORK|PR_LOG_ON_LOCALLY); + + 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", "", privilege_none, PR_ACCESS_FROM_NETWORK); + + 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; + TALLOC_CTX *mem_ctx; + + if (tdb && local_pid == getpid()) + return True; + mem_ctx = talloc_init("init_group_mapping"); + if (!mem_ctx) { + DEBUG(0,("No memory to open group mapping database\n")); + return False; + } + tdb = tdb_open_log(lock_path(mem_ctx, "group_mapping.tdb"), 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600); + talloc_destroy(mem_ctx); + if (!tdb) { + DEBUG(0,("Failed to open group mapping database\n")); + return False; + } + + local_pid = 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; +} + +/**************************************************************************** +****************************************************************************/ +BOOL add_mapping_entry(GROUP_MAP *map, int flag) +{ + TDB_DATA kbuf, dbuf; + pstring key, buf; + fstring string_sid=""; + int len; + int i; + PRIVILEGE_SET *set; + + if(!init_group_mapping()) { + DEBUG(0,("failed to initialize group mapping")); + return(False); + } + + sid_to_string(string_sid, &map->sid); + + len = tdb_pack(buf, sizeof(buf), "ddffd", + map->gid, map->sid_name_use, map->nt_name, map->comment, map->systemaccount); + + /* write the privilege list in the TDB database */ + + set=&map->priv_set; + len += tdb_pack(buf+len, sizeof(buf)-len, "d", set->count); + for (i=0; icount; i++) + len += tdb_pack(buf+len, sizeof(buf)-len, "ddd", + set->set[i].luid.low, set->set[i].luid.high, set->set[i].attr); + + 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, PRIVILEGE_SET priv_set, uint32 systemaccount) +{ + GROUP_MAP map; + + if(!init_group_mapping()) { + DEBUG(0,("failed to initialize group mapping")); + 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); + map.systemaccount=systemaccount; + + map.priv_set.count=priv_set.count; + map.priv_set.set=priv_set.set; + + pdb_add_group_mapping_entry(&map); + + return True; +} + +/**************************************************************************** +initialise a privilege list +****************************************************************************/ +void init_privilege(PRIVILEGE_SET *priv_set) +{ + priv_set->count=0; + priv_set->control=0; + priv_set->set=NULL; +} + +/**************************************************************************** +free a privilege list +****************************************************************************/ +BOOL free_privilege(PRIVILEGE_SET *priv_set) +{ + if (priv_set->count==0) { + DEBUG(100,("free_privilege: count=0, nothing to clear ?\n")); + return False; + } + + if (priv_set->set==NULL) { + DEBUG(0,("free_privilege: list ptr is NULL, very strange !\n")); + return False; + } + + safe_free(priv_set->set); + priv_set->count=0; + priv_set->control=0; + priv_set->set=NULL; + + return True; +} + +/**************************************************************************** +add a privilege to a privilege array +****************************************************************************/ +BOOL add_privilege(PRIVILEGE_SET *priv_set, LUID_ATTR set) +{ + LUID_ATTR *new_set; + + /* check if the privilege is not already in the list */ + if (check_priv_in_privilege(priv_set, set)) + return False; + + /* we can allocate memory to add the new privilege */ + + new_set=(LUID_ATTR *)Realloc(priv_set->set, (priv_set->count+1)*(sizeof(LUID_ATTR))); + if (new_set==NULL) { + DEBUG(0,("add_privilege: could not Realloc memory to add a new privilege\n")); + return False; + } + + 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; + + return True; +} + +/**************************************************************************** +add all the privileges to a privilege array +****************************************************************************/ +BOOL add_all_privilege(PRIVILEGE_SET *priv_set) +{ + LUID_ATTR set; + + set.attr=0; + set.luid.high=0; + + set.luid.low=SE_PRIV_ADD_USERS; + add_privilege(priv_set, set); + + set.luid.low=SE_PRIV_ADD_MACHINES; + add_privilege(priv_set, set); + + set.luid.low=SE_PRIV_PRINT_OPERATOR; + add_privilege(priv_set, set); + + return True; +} + +/**************************************************************************** +check if the privilege list is empty +****************************************************************************/ +BOOL check_empty_privilege(PRIVILEGE_SET *priv_set) +{ + return (priv_set->count == 0); +} + +/**************************************************************************** +check if the privilege is in the privilege list +****************************************************************************/ +BOOL check_priv_in_privilege(PRIVILEGE_SET *priv_set, LUID_ATTR set) +{ + int i; + + /* if the list is empty, obviously we can't have it */ + if (check_empty_privilege(priv_set)) + return False; + + for (i=0; icount; 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 True; + } + + return False; +} + +/**************************************************************************** +remove a privilege from a privilege array +****************************************************************************/ +BOOL remove_privilege(PRIVILEGE_SET *priv_set, LUID_ATTR set) +{ + LUID_ATTR *new_set; + LUID_ATTR *old_set; + int i,j; + + /* check if the privilege is in the list */ + if (!check_priv_in_privilege(priv_set, set)) + return False; + + /* special case if it's the only privilege in the list */ + if (priv_set->count==1) { + free_privilege(priv_set); + init_privilege(priv_set); + + return True; + } + + /* + * the privilege is there, create a new list, + * and copy the other privileges + */ + + old_set=priv_set->set; + + new_set=(LUID_ATTR *)malloc((priv_set->count-1)*(sizeof(LUID_ATTR))); + if (new_set==NULL) { + DEBUG(0,("remove_privilege: could not malloc memory for new privilege list\n")); + return False; + } + + for (i=0, j=0; icount; 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)); + safe_free(new_set); + return False; + } + + /* ok everything is fine */ + + priv_set->count--; + priv_set->set=new_set; + + safe_free(old_set); + + return True; +} + +/**************************************************************************** + Return the sid and the type of the unix group. +****************************************************************************/ + +BOOL get_group_map_from_sid(DOM_SID sid, GROUP_MAP *map, BOOL with_priv) +{ + TDB_DATA kbuf, dbuf; + pstring key; + fstring string_sid; + int ret; + int i; + PRIVILEGE_SET *set; + + if(!init_group_mapping()) { + DEBUG(0,("failed to initialize group mapping")); + 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, "ddffd", + &map->gid, &map->sid_name_use, &map->nt_name, &map->comment, &map->systemaccount); + + set=&map->priv_set; + init_privilege(set); + ret += tdb_unpack(dbuf.dptr+ret, dbuf.dsize-ret, "d", &set->count); + + DEBUG(10,("get_group_map_from_sid: %d privileges\n", map->priv_set.count)); + + set->set = NULL; + if (set->count) { + set->set=(LUID_ATTR *)smb_xmalloc(set->count*sizeof(LUID_ATTR)); + } + + for (i=0; icount; i++) + ret += tdb_unpack(dbuf.dptr+ret, dbuf.dsize-ret, "ddd", + &(set->set[i].luid.low), &(set->set[i].luid.high), &(set->set[i].attr)); + + SAFE_FREE(dbuf.dptr); + if (ret != dbuf.dsize) { + DEBUG(0,("get_group_map_from_sid: group mapping TDB corrupted ?\n")); + free_privilege(set); + return False; + } + + /* we don't want the privileges */ + if (with_priv==MAPPING_WITHOUT_PRIV) + free_privilege(set); + + sid_copy(&map->sid, &sid); + + return True; +} + +/**************************************************************************** + Return the sid and the type of the unix group. +****************************************************************************/ + +BOOL get_group_map_from_gid(gid_t gid, GROUP_MAP *map, BOOL with_priv) +{ + TDB_DATA kbuf, dbuf, newkey; + fstring string_sid; + int ret; + int i; + PRIVILEGE_SET *set; + + if(!init_group_mapping()) { + DEBUG(0,("failed to initialize group mapping")); + 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, "ddffd", + &map->gid, &map->sid_name_use, &map->nt_name, &map->comment, &map->systemaccount); + + set=&map->priv_set; + ret += tdb_unpack(dbuf.dptr+ret, dbuf.dsize-ret, "d", &set->count); + set->set = NULL; + if (set->count) { + set->set=(LUID_ATTR *)smb_xmalloc(set->count*sizeof(LUID_ATTR)); + } + + for (i=0; icount; i++) + ret += tdb_unpack(dbuf.dptr+ret, dbuf.dsize-ret, "ddd", + &(set->set[i].luid.low), &(set->set[i].luid.high), &(set->set[i].attr)); + + SAFE_FREE(dbuf.dptr); + if (ret != dbuf.dsize){ + free_privilege(set); + continue; + } + + if (gid==map->gid) { + if (!with_priv) + free_privilege(&map->priv_set); + return True; + } + + free_privilege(set); + } + + return False; +} + +/**************************************************************************** + Return the sid and the type of the unix group. +****************************************************************************/ + +BOOL get_group_map_from_ntname(char *name, GROUP_MAP *map, BOOL with_priv) +{ + TDB_DATA kbuf, dbuf, newkey; + fstring string_sid; + int ret; + int i; + PRIVILEGE_SET *set; + + if(!init_group_mapping()) { + DEBUG(0,("get_group_map_from_ntname:failed to initialize group mapping")); + 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, "ddffd", + &map->gid, &map->sid_name_use, &map->nt_name, &map->comment, &map->systemaccount); + + set=&map->priv_set; + ret += tdb_unpack(dbuf.dptr+ret, dbuf.dsize-ret, "d", &set->count); + + set->set=(LUID_ATTR *)malloc(set->count*sizeof(LUID_ATTR)); + if (set->set==NULL) { + DEBUG(0,("get_group_map_from_ntname: could not allocate memory for privileges\n")); + return False; + } + + for (i=0; icount; i++) + ret += tdb_unpack(dbuf.dptr+ret, dbuf.dsize-ret, "ddd", + &(set->set[i].luid.low), &(set->set[i].luid.high), &(set->set[i].attr)); + + SAFE_FREE(dbuf.dptr); + if (ret != dbuf.dsize) { + free_privilege(set); + continue; + } + + if (StrCaseCmp(name, map->nt_name)==0) { + if (!with_priv) + free_privilege(&map->priv_set); + return True; + } + + free_privilege(set); + } + + return False; +} + +/**************************************************************************** + Remove a group mapping entry. +****************************************************************************/ + +BOOL group_map_remove(DOM_SID sid) +{ + TDB_DATA kbuf, dbuf; + pstring key; + fstring string_sid; + + if(!init_group_mapping()) { + DEBUG(0,("failed to initialize group mapping")); + 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. +****************************************************************************/ + +BOOL enum_group_mapping(enum SID_NAME_USE sid_name_use, GROUP_MAP **rmap, + int *num_entries, BOOL unix_only, BOOL with_priv) +{ + TDB_DATA kbuf, dbuf, newkey; + fstring string_sid; + fstring group_type; + GROUP_MAP map; + GROUP_MAP *mapt; + int ret; + int entries=0; + int i; + PRIVILEGE_SET *set; + + if(!init_group_mapping()) { + DEBUG(0,("failed to initialize group mapping")); + 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, "ddffd", + &map.gid, &map.sid_name_use, &map.nt_name, &map.comment, &map.systemaccount); + + set=&map.priv_set; + init_privilege(set); + + ret += tdb_unpack(dbuf.dptr+ret, dbuf.dsize-ret, "d", &set->count); + + if (set->count!=0) { + set->set=(LUID_ATTR *)malloc(set->count*sizeof(LUID_ATTR)); + if (set->set==NULL) { + DEBUG(0,("enum_group_mapping: could not allocate memory for privileges\n")); + return False; + } + } + + for (i=0; icount; i++) + ret += tdb_unpack(dbuf.dptr+ret, dbuf.dsize-ret, "ddd", + &(set->set[i].luid.low), &(set->set[i].luid.high), &(set->set[i].attr)); + + SAFE_FREE(dbuf.dptr); + if (ret != dbuf.dsize) { + DEBUG(11,("enum_group_mapping: error in memory size\n")); + free_privilege(set); + 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)); + free_privilege(set); + continue; + } + + if (unix_only==ENUM_ONLY_MAPPED && map.gid==-1) { + DEBUG(11,("enum_group_mapping: group %s is non mapped\n", map.nt_name)); + free_privilege(set); + 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); + free_privilege(set); + 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); + mapt[entries].systemaccount=map.systemaccount; + mapt[entries].priv_set.count=set->count; + mapt[entries].priv_set.control=set->control; + mapt[entries].priv_set.set=set->set; + if (!with_priv) + free_privilege(&(mapt[entries].priv_set)); + + entries++; + } + + *num_entries=entries; + + return True; +} + + +/**************************************************************************** +convert a privilege string to a privilege array +****************************************************************************/ +void convert_priv_from_text(PRIVILEGE_SET *se_priv, char *privilege) +{ + pstring tok; + const char *p = privilege; + int i; + LUID_ATTR set; + + /* By default no privilege */ + init_privilege(se_priv); + + if (privilege==NULL) + return; + + while(next_token(&p, tok, " ", sizeof(tok)) ) { + for (i=0; i<=PRIV_ALL_INDEX; i++) { + if (StrCaseCmp(privs[i].priv, tok)==0) { + set.attr=0; + set.luid.high=0; + set.luid.low=privs[i].se_priv; + add_privilege(se_priv, set); + } + } + } +} + +/**************************************************************************** +convert a privilege array to a privilege string +****************************************************************************/ +void convert_priv_to_text(PRIVILEGE_SET *se_priv, char *privilege) +{ + int i,j; + + if (privilege==NULL) + return; + + ZERO_STRUCTP(privilege); + + if (check_empty_privilege(se_priv)) { + fstrcat(privilege, "No privilege"); + return; + } + + for(i=0; icount; i++) { + j=1; + while (privs[j].se_priv!=se_priv->set[i].luid.low && j<=PRIV_ALL_INDEX) { + j++; + } + + fstrcat(privilege, privs[j].priv); + fstrcat(privilege, " "); + } +} + + +/* + * + * 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, BOOL with_priv) +{ + struct group *grp; + + if(!init_group_mapping()) { + DEBUG(0,("failed to initialize group mapping")); + 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 */ + if(!pdb_getgrsid(map, sid, with_priv)) + 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) { + if (with_priv) + free_privilege(&map->priv_set); + return False; + } + + DEBUG(10, ("get_domain_group_from_sid: SID is a domain group\n")); + + if (map->gid==-1) { + if (with_priv) + free_privilege(&map->priv_set); + return False; + } + + DEBUG(10, ("get_domain_group_from_sid: SID is mapped to gid:%d\n",map->gid)); + + if ( (grp=getgrgid(map->gid)) == NULL) { + DEBUG(10, ("get_domain_group_from_sid: gid DOESN'T exist in UNIX security\n")); + if (with_priv) + free_privilege(&map->priv_set); + 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 with_priv) +{ + struct group *grp; + + if(!init_group_mapping()) { + DEBUG(0,("failed to initialize group mapping")); + return(False); + } + + /* The group is in the mapping table */ + if(pdb_getgrsid(map, sid, with_priv)) { + if (map->sid_name_use!=SID_NAME_ALIAS) { + if (with_priv) + free_privilege(&map->priv_set); + return False; + } + + if (map->gid==-1) { + if (with_priv) + free_privilege(&map->priv_set); + return False; + } + + if ( (grp=getgrgid(map->gid)) == NULL) { + if (with_priv) + free_privilege(&map->priv_set); + return False; + } + } else { + /* the group isn't in the mapping table. + * make one based on the unix information */ + uint32 alias_rid; + + sid_peek_rid(&sid, &alias_rid); + map->gid=pdb_group_rid_to_gid(alias_rid); + + if ((grp=getgrgid(map->gid)) == NULL) + return False; + + map->sid_name_use=SID_NAME_ALIAS; + map->systemaccount=PR_ACCESS_FROM_NETWORK; + + fstrcpy(map->nt_name, grp->gr_name); + fstrcpy(map->comment, "Local Unix Group"); + + init_privilege(&map->priv_set); + + sid_copy(&map->sid, &sid); + } + + return True; +} + +/* get a builtin group from it's SID */ + +BOOL get_builtin_group_from_sid(DOM_SID sid, GROUP_MAP *map, BOOL with_priv) +{ + struct group *grp; + + if(!init_group_mapping()) { + DEBUG(0,("failed to initialize group mapping")); + return(False); + } + + if(!pdb_getgrsid(map, sid, with_priv)) + return False; + + if (map->sid_name_use!=SID_NAME_WKN_GRP) { + if (with_priv) + free_privilege(&map->priv_set); + return False; + } + + if (map->gid==-1) { + if (with_priv) + free_privilege(&map->priv_set); + return False; + } + + if ( (grp=getgrgid(map->gid)) == NULL) { + if (with_priv) + free_privilege(&map->priv_set); + return False; + } + + return True; +} + + + +/**************************************************************************** +Returns a GROUP_MAP struct based on the gid. +****************************************************************************/ +BOOL get_group_from_gid(gid_t gid, GROUP_MAP *map, BOOL with_priv) +{ + struct group *grp; + + if(!init_group_mapping()) { + DEBUG(0,("failed to initialize group mapping")); + return(False); + } + + if ( (grp=getgrgid(gid)) == NULL) + return False; + + /* + * make a group map from scratch if doesn't exist. + */ + if (!pdb_getgrgid(map, gid, with_priv)) { + map->gid=gid; + map->sid_name_use=SID_NAME_ALIAS; + map->systemaccount=PR_ACCESS_FROM_NETWORK; + init_privilege(&map->priv_set); + + /* 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; +} + + + + +/**************************************************************************** + Get the member users of a group and + all the users who have that group as primary. + + give back an array of uid + return the grand number of users + + + TODO: sort the list and remove duplicate. JFM. + +****************************************************************************/ + +BOOL get_uid_list_of_group(gid_t gid, uid_t **uid, int *num_uids) +{ + struct group *grp; + struct passwd *pwd; + int i=0; + char *gr; + uid_t *u; + + if(!init_group_mapping()) { + DEBUG(0,("failed to initialize group mapping")); + return(False); + } + + *num_uids = 0; + *uid=NULL; + + if ( (grp=getgrgid(gid)) == NULL) + return False; + + gr = grp->gr_mem[0]; + DEBUG(10, ("getting members\n")); + + while (gr && (*gr != (char)'\0')) { + u = Realloc((*uid), sizeof(uid_t)*(*num_uids+1)); + if (!u) { + DEBUG(0,("get_uid_list_of_group: unable to enlarge uid list!\n")); + return False; + } + else (*uid) = u; + + if( (pwd=getpwnam_alloc(gr)) !=NULL) { + (*uid)[*num_uids]=pwd->pw_uid; + (*num_uids)++; + } + passwd_free(&pwd); + gr = grp->gr_mem[++i]; + } + DEBUG(10, ("got [%d] members\n", *num_uids)); + + setpwent(); + while ((pwd=getpwent()) != NULL) { + if (pwd->pw_gid==gid) { + u = Realloc((*uid), sizeof(uid_t)*(*num_uids+1)); + if (!u) { + DEBUG(0,("get_uid_list_of_group: unable to enlarge uid list!\n")); + return False; + } + else (*uid) = u; + (*uid)[*num_uids]=pwd->pw_uid; + + (*num_uids)++; + } + } + endpwent(); + DEBUG(10, ("got primary groups, members: [%d]\n", *num_uids)); + + return True; +} + +/**************************************************************************** + Create a UNIX group on demand. +****************************************************************************/ + +int smb_create_group(char *unix_group, gid_t *new_gid) +{ + pstring add_script; + int ret; + int fd = 0; + + pstrcpy(add_script, lp_addgroup_script()); + if (! *add_script) return -1; + 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); + + if (*new_gid == 0) { + /* The output was garbage. We assume nobody + will create group 0 via smbd. Now we try to + get the group via getgrnam. */ + + struct group *grp = getgrnam(unix_group); + if (grp != NULL) + *new_gid = grp->gr_gid; + else + return 1; + } + } + + return ret; +} + +/**************************************************************************** + Delete a UNIX group on demand. +****************************************************************************/ + +int smb_delete_group(char *unix_group) +{ + pstring del_script; + int ret; + + pstrcpy(del_script, lp_delgroup_script()); + if (! *del_script) return -1; + 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; +} + +/**************************************************************************** + 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; + + pstrcpy(add_script, lp_setprimarygroup_script()); + if (! *add_script) return -1; + 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; +} + +/**************************************************************************** + Add a user to a UNIX group. +****************************************************************************/ + +int smb_add_user_group(char *unix_group, char *unix_user) +{ + pstring add_script; + int ret; + + pstrcpy(add_script, lp_addusertogroup_script()); + if (! *add_script) return -1; + 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; +} + +/**************************************************************************** + 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; + + pstrcpy(del_script, lp_deluserfromgroup_script()); + if (! *del_script) return -1; + 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; +} + + +NTSTATUS pdb_default_getgrsid(struct pdb_methods *methods, GROUP_MAP *map, + DOM_SID sid, BOOL with_priv) +{ + return get_group_map_from_sid(sid, map, with_priv) ? + NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; +} + +NTSTATUS pdb_default_getgrgid(struct pdb_methods *methods, GROUP_MAP *map, + gid_t gid, BOOL with_priv) +{ + return get_group_map_from_gid(gid, map, with_priv) ? + NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; +} + +NTSTATUS pdb_default_getgrnam(struct pdb_methods *methods, GROUP_MAP *map, + char *name, BOOL with_priv) +{ + return get_group_map_from_ntname(name, map, with_priv) ? + 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, BOOL with_priv) +{ + return enum_group_mapping(sid_name_use, rmap, num_entries, unix_only, + with_priv) ? + NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; +} + diff --git a/source4/ignore.txt b/source4/ignore.txt new file mode 100644 index 0000000000..420095db04 --- /dev/null +++ b/source4/ignore.txt @@ -0,0 +1,4 @@ +all_info.out.fname +compression_info.out.*_shift +internal_information.out.* +stream_info.out.streams[i].size diff --git a/source4/include/.cvsignore b/source4/include/.cvsignore new file mode 100644 index 0000000000..f486ccaba1 --- /dev/null +++ b/source4/include/.cvsignore @@ -0,0 +1,8 @@ +build_env.h +config.h +config.h.in +includes.h.gch +proto.h +stamp-h +tdbsam2_parse_info.h +wrepld_proto.h diff --git a/source4/include/MacExtensions.h b/source4/include/MacExtensions.h new file mode 100644 index 0000000000..d09370ed9f --- /dev/null +++ b/source4/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/source4/include/ads.h b/source4/include/ads.h new file mode 100644 index 0000000000..f90983e405 --- /dev/null +++ b/source4/include/ads.h @@ -0,0 +1,215 @@ +/* + 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; + + /* 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; + } 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 4 possible types of errors the ads subsystem can produce */ +enum ads_error_type {ADS_ERROR_KRB5, ADS_ERROR_GSS, + ADS_ERROR_LDAP, ADS_ERROR_SYSTEM, ADS_ERROR_NT}; + +typedef struct { + enum ads_error_type error_type; + union err_state{ + int rc; + NTSTATUS nt_status; + } err; + /* For error_type = 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(ADS_ERROR_LDAP, rc, 0) +#define ADS_ERROR_SYSTEM(rc) ads_build_error(ADS_ERROR_SYSTEM, rc?rc:EINVAL, 0) +#define ADS_ERROR_KRB5(rc) ads_build_error(ADS_ERROR_KRB5, rc, 0) +#define ADS_ERROR_GSS(rc, minor) ads_build_error(ADS_ERROR_GSS, rc, minor) +#define ADS_ERROR_NT(rc) ads_build_nt_error(ADS_ERROR_NT,rc) + +#define ADS_ERR_OK(status) ((status.error_type == 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 + +/* 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 diff --git a/source4/include/adt_tree.h b/source4/include/adt_tree.h new file mode 100644 index 0000000000..b1bf7ad85d --- /dev/null +++ b/source4/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)(void *p); +} SORTED_TREE; + +#endif diff --git a/source4/include/asn_1.h b/source4/include/asn_1.h new file mode 100644 index 0000000000..7d4da0db0c --- /dev/null +++ b/source4/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 "\x01\x00" +#define TOK_ID_KRB_AP_REP "\x02\x00" +#define TOK_ID_KRB_ERROR "\x03\x00" +#define TOK_ID_GSS_GETMIC "\x01\x01" +#define TOK_ID_GSS_WRAP "\x02\x01" + +#endif /* _ASN_1_H */ diff --git a/source4/include/auth.h b/source4/include/auth.h new file mode 100644 index 0000000000..e37f181082 --- /dev/null +++ b/source4/include/auth.h @@ -0,0 +1,161 @@ +#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; + + /* 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; + + uint8 session_key[16]; + + uint8 first_8_lm_hash[8]; + + uint32 sam_fill_level; /* How far is this structure filled? */ + + SAM_ACCOUNT *sam_account; + + void *pam_handle; + +} auth_serversupplied_info; + +struct auth_context { + DATA_BLOB challenge; + + /* Who set this up in the first place? */ + const char *challenge_set_by; + + 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; +}; + +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; + +#endif /* _SMBAUTH_H_ */ diff --git a/source4/include/byteorder.h b/source4/include/byteorder.h new file mode 100644 index 0000000000..ed0eababdd --- /dev/null +++ b/source4/include/byteorder.h @@ -0,0 +1,175 @@ +/* + 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)(((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)) + + +/* macros for accessing SMB protocol elements */ +#define VWV(vwv) ((vwv)*2) + +#endif /* _BYTEORDER_H */ diff --git a/source4/include/charset.h b/source4/include/charset.h new file mode 100644 index 0000000000..3b3e613fd3 --- /dev/null +++ b/source4/include/charset.h @@ -0,0 +1,40 @@ +/* + 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 pulls from that charset to + * a ucs2 buffer, and a function that pushes to a ucs2 buffer + * */ + +struct charset_functions { + const char *name; + size_t (*pull)(void *, const char **inbuf, size_t *inbytesleft, + char **outbuf, size_t *outbytesleft); + size_t (*push)(void *, const char **inbuf, size_t *inbytesleft, + char **outbuf, size_t *outbytesleft); + struct charset_functions *prev, *next; +}; + diff --git a/source4/include/cli_context.h b/source4/include/cli_context.h new file mode 100644 index 0000000000..184327e7d3 --- /dev/null +++ b/source4/include/cli_context.h @@ -0,0 +1,308 @@ +/* + 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 + Copyright (C) James Myers 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the 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 _CLI_CONTEXT_H +#define _CLI_CONTEXT_H + +struct cli_tree; /* forward declare */ +struct cli_request; /* forward declare */ +struct cli_session; /* forward declare */ +struct cli_transport; /* forward declare */ + +typedef struct smb_sign_info { + void (*sign_outgoing_message)(struct cli_request *req); + BOOL (*check_incoming_message)(struct cli_request *req); + void (*free_signing_context)(struct cli_transport *transport); + void *signing_context; + + BOOL doing_signing; +} smb_sign_info; + +/* context that will be and has been negotiated between the client and server */ +struct cli_negotiate { + /* + * negotiated maximum transmit size - this is given to us by the server + */ + unsigned max_xmit; + + /* maximum number of requests that can be multiplexed */ + uint16 max_mux; + + /* the negotiatiated protocol */ + enum protocol_types protocol; + + int sec_mode; /* security mode returned by negprot */ + DATA_BLOB secblob; /* cryptkey or negTokenInit blob */ + uint32 sesskey; + + smb_sign_info sign_info; + + /* capabilities that the server reported */ + uint32 capabilities; + + int server_zone; + time_t server_time; + int readbraw_supported:1; + int writebraw_supported:1; + + const char *server_domain; +}; + +/* this is the context for a SMB socket associated with the socket itself */ +struct cli_socket { + TALLOC_CTX *mem_ctx; /* life of socket pool */ + + /* when the reference count reaches zero then the socket is destroyed */ + int reference_count; + + struct in_addr dest_ip; + + /* the port used */ + int port; + + /* the open file descriptor */ + int fd; + + /* a count of the number of packets we have received. We + * actually only care about zero/non-zero at this stage */ + unsigned pkt_count; + + /* the network address of the client */ + char *client_addr; + + /* timeout for socket operations in milliseconds. */ + int timeout; +}; + +/* + this structure allows applications to control the behaviour of the + client library +*/ +struct cli_options { + int use_oplocks:1; + int use_level2_oplocks:1; + int use_spnego:1; +}; + +/* this is the context for the client transport layer */ +struct cli_transport { + TALLOC_CTX *mem_ctx; + + /* when the reference count reaches zero then the transport is destroyed */ + int reference_count; + + /* socket level info */ + struct cli_socket *socket; + + /* the next mid to be allocated - needed for signing and + request matching */ + uint16 next_mid; + + /* negotiated protocol information */ + struct cli_negotiate negotiate; + + /* options to control the behaviour of the client code */ + struct cli_options options; + + /* is a readbraw pending? we need to handle that case + specially on receiving packets */ + int readbraw_pending:1; + + /* an idle function - if this is defined then it will be + called once every period milliseconds while we are waiting + for a packet */ + struct { + void (*func)(struct cli_transport *, void *); + void *private; + uint_t period; + } idle; + + /* the error fields from the last message */ + struct { + enum {ETYPE_NONE, ETYPE_DOS, ETYPE_NT, ETYPE_SOCKET, ETYPE_NBT} etype; + union { + struct { + uint8 eclass; + uint16 ecode; + } dos; + NTSTATUS nt_status; + enum socket_error socket_error; + unsigned nbt_error; + } e; + } error; + + struct { + /* a oplock break request handler */ + BOOL (*handler)(struct cli_transport *transport, + uint16 tid, uint16 fnum, uint8 level, void *private); + /* private data passed to the oplock handler */ + void *private; + } oplock; + + /* a list of async requests that are pending on this connection */ + struct cli_request *pending_requests; +}; + +/* this is the context for the user */ + +/* this is the context for the session layer */ +struct cli_session { + TALLOC_CTX *mem_ctx; /* life of session */ + + /* when the reference count reaches zero then the session is destroyed */ + int reference_count; + + /* transport layer info */ + struct cli_transport *transport; + + /* after a session setup the server provides us with + a vuid identifying the security context */ + uint16 vuid; + + /* default pid for this session */ + uint16 pid; +}; + +/* + cli_tree context: internal state for a tree connection. + */ +struct cli_tree { + /* life of tree tree */ + TALLOC_CTX *mem_ctx; + + /* when the reference count reaches zero then the tree is destroyed */ + int reference_count; + + /* session layer info */ + struct cli_session *session; + + uint16 tid; /* tree id, aka cnum */ + char *device; + char *fs_type; +}; + +/* the context for a single SMB request. This is passed to any request-context + * functions (similar to context.h, the server version). + * This will allow requests to be multi-threaded. */ +struct cli_request { + /* allow a request to be part of a list of requests */ + struct cli_request *next, *prev; + + /* a talloc context for the lifetime of this request */ + TALLOC_CTX *mem_ctx; + + /* a request always has a transport context, nearly always has + a session context and usually has a tree context */ + struct cli_transport *transport; + struct cli_session *session; + struct cli_tree *tree; + + /* the flags2 from the SMB request, in raw form (host byte + order). Used to parse strings */ + uint16 flags2; + + /* the NT status for this request. Set by packet receive code + or code detecting error. */ + NTSTATUS status; + + /* the sequence number of this packet - used for signing */ + unsigned seq_num; + + /* set if this is a one-way request, meaning we are not + expecting a reply from the server. */ + int one_way_request:1; + + /* the mid of this packet - used to match replies */ + uint16 mid; + + struct { + /* the raw SMB buffer, including the 4 byte length header */ + char *buffer; + + /* the size of the raw buffer, including 4 byte header */ + unsigned size; + + /* how much has been allocated - on reply the buffer is over-allocated to + prevent too many realloc() calls + */ + unsigned allocated; + + /* the start of the SMB header - this is always buffer+4 */ + char *hdr; + + /* the command words and command word count. vwv points + into the raw buffer */ + char *vwv; + unsigned wct; + + /* the data buffer and size. data points into the raw buffer */ + char *data; + unsigned data_size; + + /* ptr is used as a moving pointer into the data area + * of the packet. The reason its here and not a local + * variable in each function is that when a realloc of + * a send packet is done we need to move this + * pointer */ + char *ptr; + } in, out; + + /* information on what to do with a reply when it is received + asyncronously. If this is not setup when a reply is received then + the reply is discarded + + The private pointer is private to the caller of the client + library (the application), not private to the library + */ + struct { + void (*fn)(struct cli_request *); + void *private; + } async; +}; + +/* + cli_state: internal state used in libcli library for single-threaded callers, + i.e. a single session on a single socket. + */ +struct cli_state { + TALLOC_CTX *mem_ctx; /* life of client pool */ + struct cli_transport *transport; + struct cli_session *session; + struct cli_tree *tree; + struct substitute_context substitute; +}; + +/* useful way of catching wct errors with file and line number */ +#define CLI_CHECK_MIN_WCT(req, wcount) if ((req)->in.wct < (wcount)) { \ + DEBUG(1,("Unexpected WCT %d at %s(%d) - expected min %d\n", (req)->in.wct, __FILE__, __LINE__, wcount)); \ + req->status = NT_STATUS_INVALID_PARAMETER; \ + goto failed; \ +} + +#define CLI_CHECK_WCT(req, wcount) if ((req)->in.wct != (wcount)) { \ + DEBUG(1,("Unexpected WCT %d at %s(%d) - expected %d\n", (req)->in.wct, __FILE__, __LINE__, wcount)); \ + req->status = NT_STATUS_INVALID_PARAMETER; \ + goto failed; \ +} + +#endif /* _CLI_CONTEXT_H */ diff --git a/source4/include/client.h b/source4/include/client.h new file mode 100644 index 0000000000..015c8fb18a --- /dev/null +++ b/source4/include/client.h @@ -0,0 +1,118 @@ +/* + 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 + Copyright (C) James Myers 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the 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) +#define CLI_DFS_MAX_REFERRAL_LEVEL 3 + +#define SAFETY_MARGIN 1024 +#define LARGE_WRITEX_HDR_SIZE 65 + + +/* + * 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; + const char *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; +}; + +typedef struct referral_info +{ + int server_type; + int referral_flags; + int proximity; + int ttl; + int pathOffset; + int altPathOffset; + int nodeOffset; + char *path; + char *altPath; + char *node; + char *host; + char *share; +} referral_info; + +typedef struct dfs_info +{ + int path_consumed; + int referral_flags; + int selected_referral; + int number_referrals; + referral_info referrals[10]; +} dfs_info; + +/* Internal client error codes for cli_request_context.internal_error_code */ +#define CLI_ERR_INVALID_TRANS_RESPONSE 100 + +#define DFS_MAX_CLUSTER_SIZE 8 +/* client_context: used by cliraw callers to maintain Dfs + * state across multiple Dfs servers + */ +struct cli_client +{ + const char* sockops; + char* username; + char* password; + char* workgroup; + TALLOC_CTX *mem_ctx; + int number_members; + BOOL use_dfs; /* True if client should support Dfs */ + int connection_flags; /* see CLI_FULL_CONN.. below */ + uint16 max_xmit_frag; + uint16 max_recv_frag; + struct cli_state *cli[DFS_MAX_CLUSTER_SIZE]; +}; + +#define CLI_FULL_CONNECTION_DONT_SPNEGO 0x0001 +#define CLI_FULL_CONNECTION_USE_KERBEROS 0x0002 +#define CLI_FULL_CONNECTION_ANNONYMOUS_FALLBACK 0x0004 +#define CLI_FULL_CONNECTION_USE_DFS 0x0008 + +#include "cli_context.h" +#endif /* _CLIENT_H */ diff --git a/source4/include/clitar.h b/source4/include/clitar.h new file mode 100644 index 0000000000..b7731172d6 --- /dev/null +++ b/source4/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/source4/include/context.h b/source4/include/context.h new file mode 100644 index 0000000000..102403d009 --- /dev/null +++ b/source4/include/context.h @@ -0,0 +1,346 @@ +/* + Unix SMB/CIFS implementation. + + Copyright (C) Andrew Tridgell 2003 + Copyright (C) James J Myers 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the 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 header declares the core context structures associated with smb + sockets, tree connects, requests etc + + the idea is that we will eventually get rid of all our global + variables and instead store our stang from structures hanging off + these basic elements +*/ + +/* the current user context for a request */ +struct user_context { + /* the vuid is used to specify the security context for this + request. Note that this may not be the same vuid as we + received on the wire (for example, for share mode or guest + access) */ + uint16 vuid; + + /* the domain name, user name etc - mostly used in % substitutions */ + struct userdom_struct *user; + + struct user_struct *vuser; +}; + +/* the context for a single SMB request. This is passed to any request-context + functions */ +struct request_context { + /* the server_context contains all context specific to this SMB socket */ + struct server_context *smb; + + /* conn is only set for operations that have a valid TID */ + struct tcon_context *conn; + + /* the user context is derived from the vuid plus smb.conf options */ + struct user_context *user_ctx; + + /* a talloc context for the lifetime of this request */ + TALLOC_CTX *mem_ctx; + + /* a set of flags to control usage of the request. See REQ_CONTROL_* */ + unsigned control_flags; + + /* the smb pid is needed for locking contexts */ + uint16 smbpid; + + /* the flags from the SMB request, in raw form (host byte order) */ + uint16 flags, flags2; + + /* the system time when the request arrived */ + struct timeval request_time; + + /* this can contain a fnum from an earlier part of a chained + * message (such as an SMBOpenX), or -1 */ + int chained_fnum; + + /* how far through the chain of SMB commands have we gone? */ + unsigned chain_count; + + /* the async structure allows backend functions to delay + replying to requests. To use this, the front end must set + async.send_fn to a function to be called by the backend + when the reply is finally ready to be sent. The backend + must set async.status to the status it wants in the + reply. The backend must set the REQ_CONTROL_ASYNC + control_flag on the request to indicate that it wishes to + delay the reply + + If async.send_fn is NULL then the backend cannot ask for a + delayed reply for this request + + note that the async.private pointer is private to the front + end not the backend. The backend must not change it. + */ + struct { + void (*send_fn)(struct request_context *); + void *private; + NTSTATUS status; + } async; + + struct { + /* the raw SMB buffer, including the 4 byte length header */ + char *buffer; + + /* the size of the raw buffer, including 4 byte header */ + unsigned size; + + /* how much has been allocated - on reply the buffer is over-allocated to + prevent too many realloc() calls + */ + unsigned allocated; + + /* the start of the SMB header - this is always buffer+4 */ + char *hdr; + + /* the command words and command word count. vwv points + into the raw buffer */ + char *vwv; + unsigned wct; + + /* the data buffer and size. data points into the raw buffer */ + char *data; + unsigned data_size; + + /* ptr is used as a moving pointer into the data area + * of the packet. The reason its here and not a local + * variable in each function is that when a realloc of + * a reply packet is done we need to move this + * pointer */ + char *ptr; + } in, out; +}; + + + +/* the context associated with open files on an smb socket */ +struct files_context { + struct files_struct *files; /* open files */ + struct bitmap *file_bmap; /* bitmap used to allocate file handles */ + + /* a fsp to use when chaining */ + struct files_struct *chain_fsp; + + /* a fsp to use to save when breaking an oplock. */ + struct files_struct *oplock_save_chain_fsp; + + /* how many files are open */ + int files_used; + + /* limit for maximum open files */ + int real_max_open_files; +}; + + +/* the context associated with open tree connects on a smb socket */ +struct tree_context { + struct tcon_context *connections; + + /* number of open connections */ + struct bitmap *bmap; + int num_open; +}; + +/* context associated with currently valid session setups */ +struct users_context { + /* users from session setup */ + char *session_users; /* was a pstring */ + + /* this holds info on user ids that are already validated for this VC */ + struct user_struct *validated_users; + int next_vuid; /* initialise to VUID_OFFSET */ + int num_validated_vuids; +}; + + +/* this contains variables that should be used in % substitutions for + * smb.conf parameters */ +struct substitute_context { + char *remote_arch; + + /* our local netbios name, as give to us by the client */ + char *local_machine; + + /* the remote netbios name, as give to us by the client */ + char *remote_machine; + + /* the select remote protocol */ + char *remote_proto; + + /* the name of the client as should be displayed in + * smbstatus. Can be an IP or a netbios name */ + char *client_name; + + /* the username for %U */ + char *user_name; +}; + +/* context that has been negotiated between the client and server */ +struct negotiate_context { + /* have we already done the NBT session establishment? */ + BOOL done_nbt_session; + + /* only one negprot per connection is allowed */ + BOOL done_negprot; + + /* multiple session setups are allowed, but some parameters are + ignored in any but the first */ + BOOL done_sesssetup; + + /* + * Size of data we can send to client. Set + * by the client for all protocols above CORE. + * Set by us for CORE protocol. + */ + unsigned max_send; /* init to BUFFER_SIZE */ + + /* + * Size of the data we can receive. Set by us. + * Can be modified by the max xmit parameter. + */ + unsigned max_recv; /* init to BUFFER_SIZE */ + + /* a guess at the remote architecture. Try not to rely on this - in almost + all cases using these values is the wrong thing to do */ + enum remote_arch_types ra_type; + + /* the negotiatiated protocol */ + enum protocol_types protocol; + + /* authentication context for multi-part negprot */ + struct auth_context *auth_context; + + /* state of NTLMSSP auth */ + struct auth_ntlmssp_state *ntlmssp_state; + + /* did we tell the client we support encrypted passwords? */ + BOOL encrypted_passwords; + + /* did we send an extended security negprot reply? */ + BOOL spnego_negotiated; + + /* client capabilities */ + uint32 client_caps; +}; + +/* this is the context for a SMB socket associated with the socket itself */ +struct socket_context { + /* the open file descriptor */ + int fd; + + /* the last read error on the socket, if any (replaces smb_read_error global) */ + int read_error; + + /* a count of the number of packets we have received. We + * actually only care about zero/non-zero at this stage */ + unsigned pkt_count; + + /* the network address of the client */ + char *client_addr; +}; + + +/* this holds long term state specific to the printing subsystem */ +struct printing_context { + struct notify_queue *notify_queue_head; +}; + + +/* the server_context holds a linked list of pending requests, + * this is used for blocking locks and requests blocked due to oplock + * break requests */ +struct pending_request { + struct pending_request *next, *prev; + + /* the request itself - needs to be freed */ + struct request_context *request; +}; + +/* the timers context contains info on when we last did various + * functions */ +struct timers_context { + /* when did we last do timeout processing? */ + time_t last_timeout_processing; + + /* when did we last sent a keepalive */ + time_t last_keepalive_sent; + + /* when we last checked the smb.conf for auto-reload */ + time_t last_smb_conf_reload; +}; + + +/* the process model operations structure - contains function pointers to + the model-specific implementations of each operation */ +struct model_ops { + /* called at startup when the model is selected */ + void (*model_startup)(void); + + /* function to accept new connection */ + void (*accept_connection)(struct event_context *, struct fd_event *, time_t, uint16); + + /* function to terminate a connection */ + void (*terminate_connection)(struct server_context *smb, const char *reason); + + /* function to exit server */ + void (*exit_server)(struct server_context *smb, const char *reason); + + /* returns process or thread id */ + int (*get_id)(struct request_context *req); +}; + + +/* smb context structure. This should contain all the state + * information associated with a SMB server */ +struct server_context { + /* a talloc context for all data in this structure */ + TALLOC_CTX *mem_ctx; + + struct negotiate_context negotiate; + + struct substitute_context substitute; + + struct socket_context socket; + + struct files_context file; + + struct tree_context tree; + + struct users_context users; + + struct printing_context print; + + struct timers_context timers; + + /* the pid of the process handling this session */ + pid_t pid; + + /* pointer make to smbd daemon context */ + struct smbd_context *smbd; + + /* pointer to list of events that we are waiting on */ + struct event_context *events; + + /* process model specific operations */ + struct model_ops *model_ops; +}; + diff --git a/source4/include/debug.h b/source4/include/debug.h new file mode 100644 index 0000000000..814a79a44b --- /dev/null +++ b/source4/include/debug.h @@ -0,0 +1,49 @@ +/* + Unix SMB/CIFS implementation. + Samba debug defines + 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. +*/ + +/* the debug operations structure - contains function pointers to + various debug implementations of each operation */ +struct debug_ops { + /* function to log (using DEBUG) suspicious usage of data structure */ + void (*log_suspicious_usage)(const char* from, const char* info); + + /* function to log (using printf) suspicious usage of data structure. + * To be used in circumstances when using DEBUG would cause loop. */ + void (*print_suspicious_usage)(const char* from, const char* info); + + /* function to return process/thread id */ + uint32 (*get_task_id)(void); +}; + +void do_debug(const char *, ...) PRINTF_ATTRIBUTE(1,2); + +extern int DEBUGLEVEL; + +#define DEBUGLVL(level) ((level) <= DEBUGLEVEL) +#define DEBUG(level, body) do { if (DEBUGLVL(level)) do_debug body; } while (0) +#define DEBUGADD(level, body) DEBUG(level, body) +#define DEBUGC(class, level, body) DEBUG(level, body) +#define DEBUGADDC(class, level, body) DEBUG(level, body) +#define DEBUGTAB(n) do_debug_tab(n) + +enum debug_logtype {DEBUG_FILE, DEBUG_STDOUT, DEBUG_STDERR}; + +/* keep some debug class defines for now to avoid changing old code too much */ +#define DBGC_AUTH 0 diff --git a/source4/include/dlinklist.h b/source4/include/dlinklist.h new file mode 100644 index 0000000000..f1ceb8acf3 --- /dev/null +++ b/source4/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) \ +do { \ + if (!(list)) { \ + (list) = (p); \ + (p)->next = (p)->prev = NULL; \ + } else { \ + (list)->prev = (p); \ + (p)->next = (list); \ + (p)->prev = NULL; \ + (list) = (p); \ + }\ +} while (0) + +/* remove an element from a list - element doesn't have to be in list. */ +#define DLIST_REMOVE(list, p) \ +do { \ + if ((p) == (list)) { \ + (list) = (p)->next; \ + if (list) (list)->prev = NULL; \ + } else { \ + if ((p)->prev) (p)->prev->next = (p)->next; \ + if ((p)->next) (p)->next->prev = (p)->prev; \ + } \ + if ((p) && ((p) != (list))) (p)->next = (p)->prev = NULL; \ +} while (0) + +/* promote an element to the top of the list */ +#define DLIST_PROMOTE(list, p) \ +do { \ + DLIST_REMOVE(list, p); \ + DLIST_ADD(list, p); \ +} while (0) + +/* hook into the end of the list - needs a tmp pointer */ +#define DLIST_ADD_END(list, p, tmp) \ +do { \ + 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); \ + } \ +} while (0) + +/* demote an element to the end of the list, needs a tmp pointer */ +#define DLIST_DEMOTE(list, p, tmp) \ +do { \ + DLIST_REMOVE(list, p); \ + DLIST_ADD_END(list, p, tmp); \ +} while (0) diff --git a/source4/include/doserr.h b/source4/include/doserr.h new file mode 100644 index 0000000000..576aeda2bf --- /dev/null +++ b/source4/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/source4/include/dynconfig.h b/source4/include/dynconfig.h new file mode 100644 index 0000000000..93a182ee3d --- /dev/null +++ b/source4/include/dynconfig.h @@ -0,0 +1,39 @@ +/* + Unix SMB/CIFS implementation. + Copyright (C) 2001 by Martin Pool + Copyright (C) 2003 by Anthony Liguori + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the 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 const char *dyn_LOGFILEBASE; +extern pstring 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/source4/include/enums.h b/source4/include/enums.h new file mode 100644 index 0000000000..5be158840f --- /dev/null +++ b/source4/include/enums.h @@ -0,0 +1,64 @@ +/* + Unix SMB/CIFS implementation. + + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the 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 header declares basic enumerated types +*/ + +/* 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_SAMBA}; + +/* case handling */ +enum case_handling {CASE_LOWER,CASE_UPPER}; + diff --git a/source4/include/events.h b/source4/include/events.h new file mode 100644 index 0000000000..7d04a38a05 --- /dev/null +++ b/source4/include/events.h @@ -0,0 +1,75 @@ +/* + Unix SMB/CIFS implementation. + main select loop and event handling + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + please read the comments in events.c before modifying +*/ + +struct event_context { + /* list of filedescriptor events */ + struct fd_event { + struct fd_event *next, *prev; + int fd; + uint16 flags; /* see EVENT_FD_* flags */ + void (*handler)(struct event_context *ev, struct fd_event *fde, time_t t, uint16 flags); + void *private; + int ref_count; + } *fd_events; + + /* list of timed events */ + struct timed_event { + struct timed_event *next, *prev; + time_t next_event; + void (*handler)(struct event_context *ev, struct timed_event *te, time_t t); + void *private; + int ref_count; + } *timed_events; + + /* list of loop events - called on each select() */ + struct loop_event { + struct loop_event *next, *prev; + void (*handler)(struct event_context *ev, struct loop_event *le, time_t t); + void *private; + int ref_count; + } *loop_events; + + /* list of signal events */ + struct signal_event { + struct signal_event *next, *prev; + int signum; + void (*handler)(struct event_context *ev, struct signal_event *se, int signum, void *sigarg); + void *private; + int ref_count; + } *signal_events; + + /* the maximum file descriptor number in fd_events */ + int maxfd; + + /* information for exiting from the event loop */ + struct { + BOOL exit_now; + int code; + } exit; +}; + + +/* bits for fd_event.flags */ +#define EVENT_FD_READ 1 +#define EVENT_FD_WRITE 2 diff --git a/source4/include/genparser.h b/source4/include/genparser.h new file mode 100644 index 0000000000..f28cd78249 --- /dev/null +++ b/source4/include/genparser.h @@ -0,0 +1,78 @@ +/* + 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. +*/ + +#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/source4/include/genparser_samba.h b/source4/include/genparser_samba.h new file mode 100644 index 0000000000..172ff2362c --- /dev/null +++ b/source4/include/genparser_samba.h @@ -0,0 +1,58 @@ +/* + 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 _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}}; + +#endif /* _GENPARSER_SAMBA_H */ diff --git a/source4/include/gums.h b/source4/include/gums.h new file mode 100644 index 0000000000..ca124d7442 --- /dev/null +++ b/source4/include/gums.h @@ -0,0 +1,230 @@ +/* + 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_OBJ_DOMAIN 1 +#define GUMS_OBJ_NORMAL_USER 2 +#define GUMS_OBJ_GROUP 3 +#define GUMS_OBJ_ALIAS 4 +#define GUMS_OBJ_WORKSTATION_TRUST 5 +#define GUMS_OBJ_SERVER_TRUST 6 +#define GUMS_OBJ_DOMAIN_TRUST 7 + +typedef 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; /* user's full name string */ + char *home_dir; /* home directory string */ + char *dir_drive; /* home directory drive string */ + char *logon_script; /* logon script string */ + char *profile_path; /* profile path string */ + char *workstations; /* login from workstations string */ + char *unknown_str; /* don't know what this is, yet. */ + char *munged_dial; /* 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 */ + + uint32 unknown_3; /* 0x00ff ffff */ + + uint16 logon_divs; /* 168 - number of hours in a week */ + uint32 hours_len; /* normally 21 bytes */ + uint8 *hours; + + uint32 unknown_5; /* 0x0002 0000 */ + uint32 unknown_6; /* 0x0000 04ec */ + +} GUMS_USER; + +typedef struct gums_group +{ + uint32 count; /* Number of SIDs */ + DOM_SID **members; /* SID array */ + +} GUMS_GROUP; + +union gums_obj_p { + gums_user *user; + gums_group *group; +} + +typedef 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; /* Object Name */ + char *description; /* Object Description */ + + union gums_obj_p data; /* Object Specific data */ + +} GUMS_OBJECT; + +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_privilege +{ + TALLOC_CTX *mem_ctx; + + uint32 type; /* Object Type */ + uint32 version; /* Object Version */ + uint32 seq_num; /* Object Sequence Number */ + + LUID_ATTR *privilege; /* Privilege Type */ + char *name; /* Object Name */ + char *description; /* Object Description */ + + uint32 count; + DOM_SID **members; + +} GUMS_PRIVILEGE; + + +typedef struct gums_functions +{ + /* 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_sid_from_name) (GUMS_OBJECT **object, const char *name); + /* 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) (const 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_memberss_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 (*add_members_to_privilege) (const LUID_ATTR *priv, const DOM_SID **members); + NTSTATUS (*delete_members_from_privilege) (const LUID_ATTR *priv, const DOM_SID **members); + NTSTATUS (*enumerate_privilege_members) (DOM_SID **members, const LUID_ATTR *priv); + NTSTATUS (*get_sid_privileges) (DOM_SID **privs, const DOM_SID *sid); + /* warning!: set_privilege will overwrite a prior existing privilege if such exist */ + NTSTATUS (*set_privilege) (GUMS_PRIVILEGE *priv); + +} GUMS_FUNCTIONS; + +/* define value types */ + +#define GUMS_SET_PRIMARY_GROUP 1 +#define GUMS_SET_SEC_DESC 2 + +/* user specific type values */ +#define GUMS_SET_LOGON_TIME 10 /* keep NTTIME consecutive */ +#define GUMS_SET_LOGOFF_TIME 11 /* too ease checking */ +#define GUMS_SET_KICKOFF_TIME 13 +#define GUMS_SET_PASS_LAST_SET_TIME 14 +#define GUMS_SET_PASS_CAN_CHANGE_TIME 15 +#define GUMS_SET_PASS_MUST_CHANGE_TIME 16 /* NTTIME end */ + +#define GUMS_SET_NAME 20 /* keep strings consecutive */ +#define GUMS_SET_DESCRIPTION 21 /* too ease checking */ +#define GUMS_SET_FULL_NAME 22 +#define GUMS_SET_HOME_DIRECTORY 23 +#define GUMS_SET_DRIVE 24 +#define GUMS_SET_LOGON_SCRIPT 25 +#define GUMS_SET_PROFILE_PATH 26 +#define GUMS_SET_WORKSTATIONS 27 +#define GUMS_SET_UNKNOWN_STRING 28 +#define GUMS_SET_MUNGED_DIAL 29 /* strings end */ + +#define GUMS_SET_LM_PASSWORD 40 +#define GUMS_SET_NT_PASSWORD 41 +#define GUMS_SET_PLAINTEXT_PASSWORD 42 +#define GUMS_SET_UNKNOWN_3 43 +#define GUMS_SET_LOGON_DIVS 44 +#define GUMS_SET_HOURS_LEN 45 +#define GUMS_SET_HOURS 46 +#define GUMS_SET_UNKNOWN_5 47 +#define GUMS_SET_UNKNOWN_6 48 + +#define GUMS_SET_MUST_CHANGE_PASS 50 +#define GUMS_SET_CANNOT_CHANGE_PASS 51 +#define GUMS_SET_PASS_NEVER_EXPIRE 52 +#define GUMS_SET_ACCOUNT_DISABLED 53 +#define GUMS_SET_ACCOUNT_LOCKOUT 54 + +/*group specific type values */ +#define GUMS_ADD_SID_LIST 60 +#define GUMS_DEL_SID_LIST 61 +#define GUMS_SET_SID_LIST 62 + +#endif /* _GUMS_H */ diff --git a/source4/include/hmacmd5.h b/source4/include/hmacmd5.h new file mode 100644 index 0000000000..6b53a6fd07 --- /dev/null +++ b/source4/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/source4/include/includes.h b/source4/include/includes.h new file mode 100644 index 0000000000..f369367d82 --- /dev/null +++ b/source4/include/includes.h @@ -0,0 +1,1237 @@ +#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 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the 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 LINUX +#ifndef DEFAULT_PRINTING +#define DEFAULT_PRINTING PRINT_BSD +#endif +#ifndef PRINTCAP_NAME +#define PRINTCAP_NAME "/etc/printcap" +#endif +#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 +/* + * has to be included before any other to get + * large file support on Reliant UNIX. Yes, it's broken :-). + */ +#ifdef HAVE_UNISTD_H +#include +#endif +#endif /* RELIANTUNIX */ + +#include + +#ifdef TIME_WITH_SYS_TIME +#include +#include +#else +#ifdef HAVE_SYS_TIME_H +#include +#else +#include +#endif +#endif + +#ifdef HAVE_SYS_RESOURCE_H +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#include + +#ifdef HAVE_SYS_PARAM_H +#include +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif + +#ifdef HAVE_SYS_SOCKET_H +#include +#endif + +#ifdef HAVE_UNIXSOCKET +#include +#endif + +#ifdef HAVE_SYS_SYSCALL_H +#include +#elif HAVE_SYSCALL_H +#include +#endif + +#ifdef HAVE_STRING_H +#include +#endif + +#ifdef HAVE_STRINGS_H +#include +#endif + +#ifdef HAVE_MEMORY_H +#include +#endif + +#ifdef HAVE_MALLOC_H +#include +#endif + +#ifdef HAVE_FCNTL_H +#include +#else +#ifdef HAVE_SYS_FCNTL_H +#include +#endif +#endif + +#include + +#ifdef HAVE_LIMITS_H +#include +#endif + +#ifdef HAVE_SYS_IOCTL_H +#include +#endif + +#ifdef HAVE_SYS_FILIO_H +#include +#endif + +#include + +#ifdef HAVE_SYS_WAIT_H +#include +#endif +#ifdef HAVE_CTYPE_H +#include +#endif +#ifdef HAVE_GRP_H +#include +#endif +#ifdef HAVE_SYS_PRIV_H +#include +#endif +#ifdef HAVE_SYS_ID_H +#include +#endif + +#include + +#ifdef HAVE_UTIME_H +#include +#endif + +#ifdef HAVE_SYS_SELECT_H +#include +#endif + +#ifdef HAVE_SYS_MODE_H +/* apparently AIX needs this for S_ISLNK */ +#ifndef S_ISLNK +#include +#endif +#endif + +#ifdef HAVE_GLOB_H +#include +#endif + +#include + +#ifdef HAVE_STDARG_H +#include +#else +#include +#endif + +#include +#include +#include + +#ifdef HAVE_SYSLOG_H +#include +#else +#ifdef HAVE_SYS_SYSLOG_H +#include +#endif +#endif + +#include + +#ifdef HAVE_NETINET_TCP_H +#include +#endif + +/* + * The next three defines are needed to access the IPTOS_* options + * on some systems. + */ + +#ifdef HAVE_NETINET_IN_SYSTM_H +#include +#endif + +#ifdef HAVE_NETINET_IN_IP_H +#include +#endif + +#ifdef HAVE_NETINET_IP_H +#include +#endif + +#if defined(HAVE_TERMIOS_H) +/* POSIX terminal handling. */ +#include +#elif defined(HAVE_TERMIO_H) +/* Older SYSV terminal handling - don't use if we can avoid it. */ +#include +#elif defined(HAVE_SYS_TERMIO_H) +/* Older SYSV terminal handling - don't use if we can avoid it. */ +#include +#endif + +#if HAVE_DIRENT_H +# include +# define NAMLEN(dirent) strlen((dirent)->d_name) +#else +# define dirent direct +# define NAMLEN(dirent) (dirent)->d_namlen +# if HAVE_SYS_NDIR_H +# include +# endif +# if HAVE_SYS_DIR_H +# include +# endif +# if HAVE_NDIR_H +# include +# endif +#endif + +#ifdef HAVE_SYS_MMAN_H +#include +#endif + +#ifdef HAVE_NET_IF_H +#include +#endif + + +#ifdef HAVE_SYS_MOUNT_H +#include +#endif + +#ifdef HAVE_SYS_VFS_H +#include +#endif + +#ifdef HAVE_SYS_ACL_H +#include +#endif + +#ifdef HAVE_SYS_FS_S5PARAM_H +#include +#endif + +#if defined (HAVE_SYS_FILSYS_H) && !defined (_CRAY) +#include +#endif + +#ifdef HAVE_SYS_STATFS_H +# include +#endif + +#ifdef HAVE_DUSTAT_H +#include +#endif + +#ifdef HAVE_SYS_STATVFS_H +#include +#endif + +#ifdef HAVE_SHADOW_H +#include +#endif + +#ifdef HAVE_GETPWANAM +#include +#include +#include +#endif + +#ifdef HAVE_SYS_SECURITY_H +#include +#include +#define PASSWORD_LENGTH 16 +#endif /* HAVE_SYS_SECURITY_H */ + +#ifdef HAVE_COMPAT_H +#include +#endif + +#ifdef HAVE_STROPTS_H +#include +#endif + +#ifdef HAVE_POLL_H +#include +#endif + +#ifdef HAVE_SYS_CAPABILITY_H + +#if defined(BROKEN_REDHAT_7_SYSTEM_HEADERS) && !defined(_I386_STATFS_H) +#define _I386_STATFS_H +#define BROKEN_REDHAT_7_STATFS_WORKAROUND +#endif + +#include + +#ifdef BROKEN_REDHAT_7_STATFS_WORKAROUND +#undef _I386_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 +#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 +#endif +#if defined(HAVE_RPCSVC_YPCLNT_H) +#include +#endif +#endif /* HAVE_NETGROUP */ + +#if defined(HAVE_SYS_IPC_H) +#include +#endif /* HAVE_SYS_IPC_H */ + +#if defined(HAVE_SYS_SHM_H) +#include +#endif /* HAVE_SYS_SHM_H */ + +#ifdef HAVE_NATIVE_ICONV +#ifdef HAVE_ICONV +#include +#endif +#ifdef HAVE_GICONV +#include +#endif +#endif + +#if HAVE_KRB5_H +#include +#else +#undef HAVE_KRB5 +#endif + +#if HAVE_LBER_H +#include +#endif + +#if HAVE_LDAP_H +#include +#else +#undef HAVE_LDAP +#endif + +#if HAVE_GSSAPI_H +#include +#endif + +#if HAVE_GSSAPI_GSSAPI_H +#include +#endif + +#if HAVE_GSSAPI_GSSAPI_GENERIC_H +#include +#endif + +#if HAVE_COM_ERR_H +#include +#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 + +/* 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 + +#if defined(HAVE_LONGLONG) +#define SMB_BIG_UINT unsigned long long +#define SMB_BIG_INT long long +#define SBVAL(p, ofs, v) (SIVAL(p,ofs,(v)&0xFFFFFFFF), SIVAL(p,(ofs)+4,(v)>>32)) +#define BVAL(p, ofs) (IVAL(p,ofs) | (((SMB_BIG_UINT)IVAL(p,(ofs)+4)) << 32)) +#else +#define SMB_BIG_UINT unsigned long +#define SMB_BIG_INT long +#define SBVAL(p, ofs, v) (SIVAL(p,ofs,v),SIVAL(p,(ofs)+4,0)) +#define BVAL(p, ofs) IVAL(p,ofs) +#endif + +#define SMB_BIG_UINT_BITS (sizeof(SMB_BIG_UINT)*8) + +#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 "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 "trans2.h" +#include "ioctl.h" +#include "nterr.h" +#include "messages.h" +#include "charset.h" +#include "dynconfig.h" +#include "adt_tree.h" + +#include "util_getent.h" + +#include "version.h" +#include "smb.h" +#include "nameserv.h" +#include "secrets.h" + +#include "byteorder.h" + +#include "ntdomain.h" + +#include "msdfs.h" + +#include "mapping.h" + +#include "rap.h" + +#include "md5.h" +#include "hmacmd5.h" + +#include "ntlmssp.h" + +#include "auth.h" + +#include "passdb.h" + +#include "sam.h" + +#include "session.h" + +#include "asn_1.h" + +#include "popt.h" + +#include "mangle.h" + +#include "nsswitch/winbind_client.h" + +#include "genparser.h" + +#include "mutex.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/nss.h" + +/* forward declaration from printing.h to get around + header file dependencies */ + +struct printjob; + +/***** automatically generated prototypes *****/ +#include "proto.h" + +/* String routines */ + +#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 MAP_FILE +#define MAP_FILE 0 +#endif + +#if (!defined(WITH_NISPLUS) && !defined(WITH_LDAP) && !defined(WITH_TDB_SAM)) +#define USE_SMBPASS_DB 1 +#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 MAXPATHLEN +#define MAXPATHLEN 256 +#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_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 + +#if !defined(HAVE_BZERO) && defined(HAVE_MEMSET) +#define bzero(a,b) memset((a),'\0',(b)) +#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 +#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 +#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 + +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 +#define VA_COPY(dest, src) (dest) = (src) +#endif + +#ifndef HAVE_TIMEGM +time_t timegm(struct tm *tm); +#endif + +#if defined(VALGRIND) +#define strlen(x) valgrind_strlen(x) +#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 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 krb5_get_smb_session_key(krb5_context context, krb5_auth_context auth_context, uint8 session_key[16]); +#endif /* HAVE_KRB5 */ + +#endif /* _INCLUDES_H */ + diff --git a/source4/include/interfaces.h b/source4/include/interfaces.h new file mode 100644 index 0000000000..3b786f1ebc --- /dev/null +++ b/source4/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/source4/include/intl.h b/source4/include/intl.h new file mode 100644 index 0000000000..5b56d9aa2c --- /dev/null +++ b/source4/include/intl.h @@ -0,0 +1,24 @@ +/* + 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) diff --git a/source4/include/ioctl.h b/source4/include/ioctl.h new file mode 100644 index 0000000000..272004d3dc --- /dev/null +++ b/source4/include/ioctl.h @@ -0,0 +1,30 @@ +/* + Unix SMB/CIFS implementation. + ioctl and fsctl definitions + + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +/* ioctl codes */ +#define IOCTL_QUERY_JOB_INFO 0x530060 + + +/* filesystem control codes */ +#define FSCTL_FILESYSTEM 0x90000 +#define FSCTL_SET_SPARSE (FSCTL_FILESYSTEM | 0xc4) + diff --git a/source4/include/libsmb_internal.h b/source4/include/libsmb_internal.h new file mode 100644 index 0000000000..21fe47d4b2 --- /dev/null +++ b/source4/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/source4/include/libsmbclient.h b/source4/include/libsmbclient.h new file mode 100644 index 0000000000..0c905edcbc --- /dev/null +++ b/source4/include/libsmbclient.h @@ -0,0 +1,1073 @@ +/*===================================================================== + 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 + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the 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 +#include +#include + +#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]; +}; + + +/**@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, + char * server, char * share, + char * workgroup, 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, char * server, + char * share, char * workgroup, 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); + + /** 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 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 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); + + +#endif /* SMBCLIENT_H_INCLUDED */ diff --git a/source4/include/local.h b/source4/include/local.h new file mode 100644 index 0000000000..4515bd83e0 --- /dev/null +++ b/source4/include/local.h @@ -0,0 +1,226 @@ +/* Copyright (C) 1995-1998 Samba-Team */ +/* Copyright (C) 1998 John H Terpstra */ + +/* local definitions for file server */ +#ifndef _LOCAL_H +#define _LOCAL_H + +/* The default workgroup - usually overridden in smb.conf */ +#ifndef DEFAULT_WORKGROUP +#define DEFAULT_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 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 +#endif diff --git a/source4/include/mangle.h b/source4/include/mangle.h new file mode 100644 index 0000000000..769278d828 --- /dev/null +++ b/source4/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/source4/include/mapping.h b/source4/include/mapping.h new file mode 100644 index 0000000000..d4f2d28e6a --- /dev/null +++ b/source4/include/mapping.h @@ -0,0 +1,61 @@ +/* + * 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 PRIV_ALL_INDEX 5 + +#define SE_PRIV_NONE 0x0000 +#define SE_PRIV_ADD_MACHINES 0x0006 +#define SE_PRIV_SEC_PRIV 0x0008 +#define SE_PRIV_TAKE_OWNER 0x0009 +#define SE_PRIV_ADD_USERS 0xff01 +#define SE_PRIV_PRINT_OPERATOR 0xff03 +#define SE_PRIV_ALL 0xffff + +#define ENUM_ONLY_MAPPED True +#define ENUM_ALL_MAPPED False + +#define MAPPING_WITH_PRIV True +#define MAPPING_WITHOUT_PRIV False + +#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 + + +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; + uint32 systemaccount; + PRIVILEGE_SET priv_set; +} GROUP_MAP; + +typedef struct _PRIVS { + uint32 se_priv; + const char *priv; + const char *description; +} PRIVS; + diff --git a/source4/include/md5.h b/source4/include/md5.h new file mode 100644 index 0000000000..6665171e7c --- /dev/null +++ b/source4/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/source4/include/messages.h b/source4/include/messages.h new file mode 100644 index 0000000000..ce167a772d --- /dev/null +++ b/source4/include/messages.h @@ -0,0 +1,75 @@ +/* + 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 + +/* Dump out the talloc useage. */ +#define MSG_REQ_TALLOC_USAGE 14 +#define MSG_TALLOC_USAGE 15 + +/* 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/source4/include/msdfs.h b/source4/include/msdfs.h new file mode 100644 index 0000000000..1bfff9ad53 --- /dev/null +++ b/source4/include/msdfs.h @@ -0,0 +1,77 @@ +/* + 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);; } + + +#endif /* _MSDFS_H */ diff --git a/source4/include/mutex.h b/source4/include/mutex.h new file mode 100644 index 0000000000..c3e146d415 --- /dev/null +++ b/source4/include/mutex.h @@ -0,0 +1,79 @@ +#ifndef _MUTEX_H_ +#define _MUTEX_H_ +/* + Unix SMB/CIFS implementation. + Samba mutex functions + Copyright (C) Andrew Tridgell 2003 + Copyright (C) James J Myers 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the 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 add a new mutex, add it to enum mutex_id + */ +enum mutex_id { MUTEX_SMBD, /* global smbd lock */ + MUTEX_TALLOC, /* global talloc.c lock */ + MUTEX_DEBUG, /* global debug.c lock */ + MUTEX_TANK, /* vfs_tank lock */ + + MUTEX_MAX /* this MUST be kept last */ +}; + +/* To add a new read/write lock, add it to enum rwlock_id + */ +enum rwlock_id { RWLOCK_SMBD, /* global smbd lock */ + + RWLOCK_MAX /* this MUST be kept last */ +}; + +#define MUTEX_LOCK_BY_ID(mutex_index) mutex_lock_by_id(mutex_index, #mutex_index) +#define MUTEX_UNLOCK_BY_ID(mutex_index) mutex_unlock_by_id(mutex_index, #mutex_index) +#define MUTEX_INIT(mutex, name) mutex_init(mutex, #name) +#define MUTEX_DESTROY(mutex, name) mutex_destroy(mutex, #name) +#define MUTEX_LOCK(mutex, name) mutex_lock(mutex, #name) +#define MUTEX_UNLOCK(mutex, name) mutex_unlock(mutex, #name) + +#define RWLOCK_INIT(rwlock, name) rwlock_init(rwlock, #name) +#define RWLOCK_DESTROY(rwlock, name) rwlock_destroy(rwlock, #name) +#define RWLOCK_LOCK_WRITE(rwlock, name) rwlock_lock_write(rwlock, #name) +#define RWLOCK_LOCK_READ(rwlock, name) rwlock_lock_read(rwlock, #name) +#define RWLOCK_UNLOCK(rwlock, name) rwlock_unlock(rwlock, #name) + + + +/* this null typedef ensures we get the types right and avoids the + pitfalls of void* */ +typedef struct { + void *mutex; +} mutex_t; +typedef struct { + void *rwlock; +} rwlock_t; + +/* the mutex model operations structure - contains function pointers to + the model-specific implementations of each operation */ +struct mutex_ops { + int (*mutex_init)(mutex_t *mutex, const char *name); + int (*mutex_lock)(mutex_t *mutex, const char *name); + int (*mutex_unlock)(mutex_t *mutex, const char *name); + int (*mutex_destroy)(mutex_t *mutex, const char *name); + int (*rwlock_init)(rwlock_t *rwlock, const char *name); + int (*rwlock_lock_write)(rwlock_t *rwlock, const char *name); + int (*rwlock_lock_read)(rwlock_t *rwlock, const char *name); + int (*rwlock_unlock)(rwlock_t *rwlock, const char *name); + int (*rwlock_destroy)(rwlock_t *rwlock, const char *name); +}; + +#endif /* ndef _MUTEX_H_ */ diff --git a/source4/include/nameserv.h b/source4/include/nameserv.h new file mode 100644 index 0000000000..7611fdfb8d --- /dev/null +++ b/source4/include/nameserv.h @@ -0,0 +1,644 @@ +#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 + { +#if 0 + ubi_trNode node[1]; +#endif + 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 + { +#if 0 + ubi_dlNode node[1]; +#endif + pstring lmb_name; + pstring 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. */ + fstring work_group; + int token; /* Used when communicating with backup browsers. */ + fstring 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. */ +#if 0 + ubi_trRoot namelist[1]; /* List of netbios names. */ +#endif + 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/source4/include/nt_printing.h b/source4/include/nt_printing.h new file mode 100644 index 0000000000..ca65a40d48 --- /dev/null +++ b/source4/include/nt_printing.h @@ -0,0 +1,482 @@ +/* + 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; + +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/source4/include/nt_status.h b/source4/include/nt_status.h new file mode 100644 index 0000000000..9747f73eb1 --- /dev/null +++ b/source4/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/source4/include/ntdomain.h b/source4/include/ntdomain.h new file mode 100644 index 0000000000..62608b2d09 --- /dev/null +++ b/source4/include/ntdomain.h @@ -0,0 +1,379 @@ +/* + 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 + +/* 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; + +}; + +/* + * DCE/RPC-specific samba-internal-specific handling of data on + * NamedPipes. + * + */ + +typedef struct pipes_struct +{ + struct pipes_struct *next, *prev; + + struct tcon_context *conn; + uint16 vuid; /* points to the unauthenticated user that opened this pipe. */ + + fstring name; + fstring pipe_srv_name; + + 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. */ + + /* + * Windows user info. + */ + fstring user_name; + fstring domain; + fstring wks; + + /* + * Unix user name and credentials. + */ + + fstring pipe_user_name; + struct current_user pipe_user; + + uint8 session_key[16]; + + /* + * 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; + struct tcon_context *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, + struct tcon_context *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; + +struct acct_info +{ + fstring acct_name; /* account name */ + fstring acct_desc; /* account name */ + uint32 rid; /* domain-relative RID */ +}; + +/* + * 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" + +/* 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" + +#endif /* _NT_DOMAIN_H */ diff --git a/source4/include/nterr.h b/source4/include/nterr.h new file mode 100644 index 0000000000..1c052eb286 --- /dev/null +++ b/source4/include/nterr.h @@ -0,0 +1,570 @@ +/* + 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_NO_SUCH_JOB NT_STATUS(0xC0000000 | 0xEDE) /* scheduler */ + + +/* I use NT_STATUS_FOOBAR when I have no idea what error code to use - + * this means we need a torture test */ +#define NT_STATUS_FOOBAR NT_STATUS_UNSUCCESSFUL + +#endif /* _NTERR_H */ + + diff --git a/source4/include/ntlmssp.h b/source4/include/ntlmssp.h new file mode 100644 index 0000000000..f0278ffece --- /dev/null +++ b/source4/include/ntlmssp.h @@ -0,0 +1,133 @@ +/* + 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_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_DOMAIN 0x01 +#define NTLMSSP_NAME_TYPE_SERVER 0x02 +#define NTLMSSP_NAME_TYPE_DOMAIN_DNS 0x03 +#define NTLMSSP_NAME_TYPE_SERVER_DNS 0x04 + +typedef struct ntlmssp_state +{ + TALLOC_CTX *mem_ctx; + enum NTLMSSP_ROLE role; + BOOL unicode; + char *user; + char *domain; + char *workstation; + DATA_BLOB lm_resp; + DATA_BLOB nt_resp; + DATA_BLOB chal; + void *auth_context; + const uint8 *(*get_challenge)(struct ntlmssp_state *ntlmssp_state); + NTSTATUS (*check_password)(struct ntlmssp_state *ntlmssp_state); + + const char *(*get_global_myname)(void); + const char *(*get_domain)(void); + + int server_role; + uint32 expected_state; +} NTLMSSP_STATE; + +typedef struct ntlmssp_client_state +{ + TALLOC_CTX *mem_ctx; + unsigned int ref_count; + + BOOL unicode; + BOOL use_ntlmv2; + char *user; + char *domain; + char *workstation; + char *password; + + const char *(*get_global_myname)(void); + const char *(*get_domain)(void); + + DATA_BLOB chal; + DATA_BLOB lm_resp; + DATA_BLOB nt_resp; + DATA_BLOB session_key; + + uint32 neg_flags; + + /* SMB Signing */ + + uint32 ntlmssp_seq_num; + + /* ntlmv2 */ + char cli_sign_const[16]; + char cli_seal_const[16]; + char srv_sign_const[16]; + char srv_seal_const[16]; + + unsigned char cli_sign_hash[258]; + unsigned char cli_seal_hash[258]; + unsigned char srv_sign_hash[258]; + unsigned char srv_seal_hash[258]; + + /* ntlmv1 */ + unsigned char ntlmssp_hash[258]; + +} NTLMSSP_CLIENT_STATE; + diff --git a/source4/include/ntvfs.h b/source4/include/ntvfs.h new file mode 100644 index 0000000000..edec2a7e53 --- /dev/null +++ b/source4/include/ntvfs.h @@ -0,0 +1,86 @@ +/* + Unix SMB/CIFS implementation. + NTVFS structures and defines + 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. +*/ + +/* modules can use the following to determine if the interface has changed */ +#define NTVFS_INTERFACE_VERSION 1 + + + +/* each backend has to be one one of the following 3 basic types. In + * earlier versions of Samba backends needed to handle all types, now + * we implement them separately. */ +enum ntvfs_type {NTVFS_DISK, NTVFS_PRINT, NTVFS_IPC}; + + +/* the ntvfs operations structure - contains function pointers to + the backend implementations of each operation */ +struct ntvfs_ops { + /* initial setup */ + NTSTATUS (*connect)(struct request_context *req, const char *sharename); + NTSTATUS (*disconnect)(struct tcon_context *conn); + + /* path operations */ + NTSTATUS (*unlink)(struct request_context *req, struct smb_unlink *unl); + NTSTATUS (*chkpath)(struct request_context *req, struct smb_chkpath *cp); + NTSTATUS (*qpathinfo)(struct request_context *req, union smb_fileinfo *st); + NTSTATUS (*setpathinfo)(struct request_context *req, union smb_setfileinfo *st); + NTSTATUS (*open)(struct request_context *req, union smb_open *oi); + NTSTATUS (*mkdir)(struct request_context *req, union smb_mkdir *md); + NTSTATUS (*rmdir)(struct request_context *req, struct smb_rmdir *rd); + NTSTATUS (*rename)(struct request_context *req, struct smb_rename *ren); + NTSTATUS (*copy)(struct request_context *req, struct smb_copy *cp); + + /* directory search */ + NTSTATUS (*search_first)(struct request_context *req, union smb_search_first *io, void *private, + BOOL (*callback)(void *private, union smb_search_data *file)); + NTSTATUS (*search_next)(struct request_context *req, union smb_search_next *io, void *private, + BOOL (*callback)(void *private, union smb_search_data *file)); + NTSTATUS (*search_close)(struct request_context *req, union smb_search_close *io); + + /* operations on open files */ + NTSTATUS (*ioctl)(struct request_context *req, struct smb_ioctl *io); + NTSTATUS (*read)(struct request_context *req, union smb_read *io); + NTSTATUS (*write)(struct request_context *req, union smb_write *io); + NTSTATUS (*seek)(struct request_context *req, struct smb_seek *io); + NTSTATUS (*flush)(struct request_context *req, struct smb_flush *flush); + NTSTATUS (*close)(struct request_context *req, union smb_close *io); + NTSTATUS (*exit)(struct request_context *req); + NTSTATUS (*lock)(struct request_context *req, union smb_lock *lck); + NTSTATUS (*setfileinfo)(struct request_context *req, union smb_setfileinfo *info); + NTSTATUS (*qfileinfo)(struct request_context *req, union smb_fileinfo *info); + + /* filesystem operations */ + NTSTATUS (*fsinfo)(struct request_context *req, union smb_fsinfo *fs); + + /* printing specific operations */ + NTSTATUS (*lpq)(struct request_context *req, union smb_lpq *lpq); + + /* trans interfaces - only used by CIFS backend to prover complete passthru for testing */ + NTSTATUS (*trans2)(struct request_context *req, struct smb_trans2 *trans2); +}; + + +/* this structure is used by backends to determine the size of some critical types */ +struct ntvfs_critical_sizes { + int sizeof_ntvfs_ops; + int sizeof_SMB_OFF_T; + int sizeof_tcon_context; + int sizeof_request_context; +}; diff --git a/source4/include/passdb.h b/source4/include/passdb.h new file mode 100644 index 0000000000..06409aa34e --- /dev/null +++ b/source4/include/passdb.h @@ -0,0 +1,155 @@ +/* + Unix SMB/CIFS implementation. + passdb structures and parameters + Copyright (C) Gerald Carter 2001 + Copyright (C) Luke Kenneth Casson Leighton 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 _PASSDB_H +#define _PASSDB_H + + +/***************************************************************** + 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 4 + +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); + + NTSTATUS (*pdb_getgrsid)(struct pdb_context *context, GROUP_MAP *map, + DOM_SID sid, BOOL with_priv); + + NTSTATUS (*pdb_getgrgid)(struct pdb_context *context, GROUP_MAP *map, + gid_t gid, BOOL with_priv); + + NTSTATUS (*pdb_getgrnam)(struct pdb_context *context, GROUP_MAP *map, + char *name, BOOL with_priv); + + 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, BOOL with_priv); + + 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); + + NTSTATUS (*getgrsid)(struct pdb_methods *methods, GROUP_MAP *map, + DOM_SID sid, BOOL with_priv); + + NTSTATUS (*getgrgid)(struct pdb_methods *methods, GROUP_MAP *map, + gid_t gid, BOOL with_priv); + + NTSTATUS (*getgrnam)(struct pdb_methods *methods, GROUP_MAP *map, + char *name, BOOL with_priv); + + 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, BOOL with_priv); + + void *private_data; /* Private data of some kind */ + + void (*free_private_data)(void **); + +} 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; +}; + +#endif /* _PASSDB_H */ diff --git a/source4/include/popt_common.h b/source4/include/popt_common.h new file mode 100644 index 0000000000..57850bf682 --- /dev/null +++ b/source4/include/popt_common.h @@ -0,0 +1,48 @@ +/* + 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; +}; + +extern struct user_auth_info cmdline_auth_info; + +#endif /* _POPT_COMMON_H */ diff --git a/source4/include/printing.h b/source4/include/printing.h new file mode 100644 index 0000000000..229b2e6923 --- /dev/null +++ b/source4/include/printing.h @@ -0,0 +1,102 @@ +#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. +*/ + +#include "includes.h" + +/* + 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" + + +struct notify_queue { + struct notify_queue *next, *prev; + struct spoolss_notify_msg *msg; + char *buf; + size_t buflen; +}; + + +#endif /* PRINTING_H_ */ diff --git a/source4/include/process_model.h b/source4/include/process_model.h new file mode 100644 index 0000000000..0b8acfc9fa --- /dev/null +++ b/source4/include/process_model.h @@ -0,0 +1,49 @@ +/* + Unix SMB/CIFS implementation. + process model structures and defines + Copyright (C) Andrew Tridgell 2003 + Copyright (C) James J Myers 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the 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 can use the following to determine if the interface has changed */ +#define MODEL_INTERFACE_VERSION 1 + +/* the process model operations structure - contains function pointers to + the model-specific implementations of each operation */ +struct model_ops { + /* setup handler functions for select */ + void (*setup_handlers)(struct smbd_context *smbd, struct socket_select *socket_sel); + + /* function to reload services if necessary */ + void (*check_sighup)(struct smbd_context *smbd); + + /* function to accept new connection */ + BOOL (*accept_connection)(struct smbd_context *smbd, void **private, + int fd, enum socket_state *state); + + /* function to terminate a connection */ + void (*terminate_connection)( struct server_context *smb, const char *reason); + + /* function to exit server */ + void (*exit_server)(struct server_context *smb, const char *reason); + + /* synchronization operations */ + int (*mutex_init) (pthread_mutex_t *mutex, const pthread_mutexattr_t *mutex_attr); + int (*mutex_lock) (pthread_mutex_t *mutex); + int (*mutex_unlock) (pthread_mutex_t *mutex); + int (*mutex_destroy) (pthread_mutex_t *mutex); +}; diff --git a/source4/include/pstring.h b/source4/include/pstring.h new file mode 100644 index 0000000000..92870e4cae --- /dev/null +++ b/source4/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/source4/include/rap.h b/source4/include/rap.h new file mode 100755 index 0000000000..993dfa7e33 --- /dev/null +++ b/source4/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/source4/include/rpc_brs.h b/source4/include/rpc_brs.h new file mode 100644 index 0000000000..cd0928d470 --- /dev/null +++ b/source4/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/source4/include/rpc_client.h b/source4/include/rpc_client.h new file mode 100644 index 0000000000..bce9ec7f27 --- /dev/null +++ b/source4/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/source4/include/rpc_creds.h b/source4/include/rpc_creds.h new file mode 100644 index 0000000000..3022b17289 --- /dev/null +++ b/source4/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/source4/include/rpc_dce.h b/source4/include/rpc_dce.h new file mode 100644 index 0000000000..6a8c650650 --- /dev/null +++ b/source4/include/rpc_dce.h @@ -0,0 +1,320 @@ +/* + 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 and level. */ +#define NTLMSSP_AUTH_TYPE 0xa +#define NTLMSSP_AUTH_LEVEL 0x6 + +/* Maximum PDU fragment size. */ +#define MAX_PDU_FRAG_LEN 0x1630 +/* #define MAX_PDU_FRAG_LEN 0x10b8 this is what w2k sets */ + +/* + * Actual structure of a DCE UUID + */ + +typedef struct rpc_uuid +{ + uint32 time_low; + uint16 time_mid; + uint16 time_hi_and_version; + uint8 remaining[8]; +} RPC_UUID; + +#define RPC_UUID_LEN 16 + +/* RPC_IFACE */ +typedef struct rpc_iface_info +{ + RPC_UUID uuid; /* 16 bytes of rpc interface identification */ + uint32 version; /* the interface version number */ + +} RPC_IFACE; + +#define RPC_IFACE_LEN (RPC_UUID_LEN + 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; /* 0 - 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 stub_type_len; /* don't know */ + uint8 padding; /* padding */ + + uint32 unknown; /* pointer */ + +} RPC_HDR_AUTH; + +#define RPC_HDR_AUTH_LEN 8 + +/* 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" */ + uint32 msg_type; /* NTLMSSP_MESSAGE_TYPE (1,2,3) */ + +} 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/source4/include/rpc_dfs.h b/source4/include/rpc_dfs.h new file mode 100644 index 0000000000..39316a5d54 --- /dev/null +++ b/source4/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/source4/include/rpc_ds.h b/source4/include/rpc_ds.h new file mode 100644 index 0000000000..c01d10554e --- /dev/null +++ b/source4/include/rpc_ds.h @@ -0,0 +1,91 @@ +/* + 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 + + +/* macros for RPC's */ + +#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 + +typedef struct +{ + uint16 machine_role; + uint16 unknown; /* 0x6173 -- maybe just alignment? */ + + uint32 flags; + + uint32 netbios_ptr; + uint32 dnsname_ptr; + uint32 forestname_ptr; + + GUID domain_guid; + + UNISTR2 netbios_domain; + /* these 2 might be reversed in order. I can't tell from + my tests as both values are the same --jerry */ + UNISTR2 dns_domain; + UNISTR2 forest_domain; +} 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; + + + + +#endif /* _RPC_DS_H */ diff --git a/source4/include/rpc_lsa.h b/source4/include/rpc_lsa.h new file mode 100644 index 0000000000..c091e73321 --- /dev/null +++ b/source4/include/rpc_lsa.h @@ -0,0 +1,760 @@ +/* + 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 */ + +#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; + + GUID 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; +} 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; +} 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_ENUM_ACCT_WITH_RIGHT - LSA enum accounts with right */ +typedef struct +{ + POLICY_HND pol; + STRHDR right_hdr; + UNISTR2 right; +} LSA_Q_ENUM_ACCT_WITH_RIGHT; + +/* LSA_R_ENUM_ACCT_WITH_RIGHT - LSA enum accounts with right */ +typedef struct +{ + uint32 count; + SID_ARRAY sids; + NTSTATUS status; +} LSA_R_ENUM_ACCT_WITH_RIGHT; + + +/* 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_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 LUID +{ + uint32 low; + uint32 high; +} LUID; + +typedef struct LUID_ATTR +{ + LUID luid; + uint32 attr; +} LUID_ATTR ; + +typedef struct privilege_set +{ + uint32 count; + uint32 control; + LUID_ATTR *set; +} PRIVILEGE_SET; + +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/source4/include/rpc_misc.h b/source4/include/rpc_misc.h new file mode 100644 index 0000000000..06ad760c58 --- /dev/null +++ b/source4/include/rpc_misc.h @@ -0,0 +1,429 @@ +/* + 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 + + + +/* 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) + +/* + * 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 undoc; + 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 undoc; + 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 undoc; + 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; + + +#endif /* _RPC_MISC_H */ diff --git a/source4/include/rpc_netlogon.h b/source4/include/rpc_netlogon.h new file mode 100644 index 0000000000..fb849f8238 --- /dev/null +++ b/source4/include/rpc_netlogon.h @@ -0,0 +1,910 @@ +/* + 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_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]; /* 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 */ + + 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_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/source4/include/rpc_parse.h b/source4/include/rpc_parse.h new file mode 100644 index 0000000000..73fbcb2b1b --- /dev/null +++ b/source4/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/source4/include/rpc_reg.h b/source4/include/rpc_reg.h new file mode 100644 index 0000000000..46ec88283d --- /dev/null +++ b/source4/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/source4/include/rpc_samr.h b/source4/include/rpc_samr.h new file mode 100644 index 0000000000..e1fa9c06bc --- /dev/null +++ b/source4/include/rpc_samr.h @@ -0,0 +1,1867 @@ +/* + 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) Anthony Liguori 2002 + Copyright (C) Jim McDonough 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_UNKNOWN_2D 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 _DISP_USER_INFO { + SAM_ACCOUNT *sam; +} DISP_USER_INFO; + +typedef struct _DISP_GROUP_INFO { + DOMAIN_GRP *grp; +} DISP_GROUP_INFO; + + +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 unknown_3; /* 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 */ + + uint32 unknown_5; /* 0x0001 0000 */ + + 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) */ + + uint32 unknown_3; /* 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 */ + + uint32 unknown_5; /* 0x0002 0000 */ + + 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 +{ + 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_UNKNOWN_2D */ +typedef struct q_samr_unknown_2d_info +{ + POLICY_HND dom_pol; /* policy handle */ + DOM_SID2 sid; /* SID */ + +} SAMR_Q_UNKNOWN_2D; + + +/* SAMR_R_UNKNOWN_2D - probably an open */ +typedef struct r_samr_unknown_2d_info +{ + NTSTATUS status; /* return status */ + +} SAMR_R_UNKNOWN_2D; + + + +/* 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/source4/include/rpc_secdes.h b/source4/include/rpc_secdes.h new file mode 100644 index 0000000000..70191901c7 --- /dev/null +++ b/source4/include/rpc_secdes.h @@ -0,0 +1,462 @@ +/* + 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 */ +#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_SELF_RELATIVE 0x8000 +/* + * 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 + +/* security information */ +#define OWNER_SECURITY_INFORMATION 0x00000001 +#define GROUP_SECURITY_INFORMATION 0x00000002 +#define DACL_SECURITY_INFORMATION 0x00000004 +#define SACL_SECURITY_INFORMATION 0x00000008 + +#define ALL_SECURITY_INFORMATION (OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION|\ + DACL_SECURITY_INFORMATION|SACL_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 */ + GUID obj_guid; /* object GUID */ + GUID 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) + + +/* directory specific access rights */ +#define SA_RIGHT_DIR_LIST 0x0001 +#define SA_RIGHT_DIR_ADD_FILE 0x0002 +#define SA_RIGHT_DIR_ADD_SUBDIRECTORY 0x0004 +#define SA_RIGHT_DIR_TRAVERSE 0x0020 +#define SA_RIGHT_DIR_DELETE_CHILD 0x0040 + + +/* SAM Object specific access rights */ + +#define SA_RIGHT_SAM_UNKNOWN_1 0x00000001 +#define SA_RIGHT_SAM_SHUTDOWN_SERVER 0x00000002 +#define SA_RIGHT_SAM_UNKNOWN_4 0x00000004 +#define SA_RIGHT_SAM_UNKNOWN_8 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_UNKNOWN_8 | \ + SA_RIGHT_SAM_UNKNOWN_4 | \ + SA_RIGHT_SAM_SHUTDOWN_SERVER) + +#define GENERIC_RIGHTS_SAM_EXECUTE \ + (STANDARD_RIGHTS_EXECUTE_ACCESS | \ + SA_RIGHT_SAM_OPEN_DOMAIN | \ + SA_RIGHT_SAM_UNKNOWN_1) + + +/* 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_UNKNOWN_200 0x00000200 +#define SA_RIGHT_USER_UNKNOWN_400 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_UNKNOWN_200 | \ + 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/source4/include/rpc_spoolss.h b/source4/include/rpc_spoolss.h new file mode 100755 index 0000000000..c2e3d92787 --- /dev/null +++ b/source4/include/rpc_spoolss.h @@ -0,0 +1,2228 @@ +/* + 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_NETWORK) + +#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; + +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/source4/include/rpc_srvsvc.h b/source4/include/rpc_srvsvc.h new file mode 100644 index 0000000000..94d23bb4bc --- /dev/null +++ b/source4/include/rpc_srvsvc.h @@ -0,0 +1,948 @@ +/* + 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; + +typedef struct share_info_1005_info +{ + uint32 dfs_root_flag; +} 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/source4/include/rpc_wkssvc.h b/source4/include/rpc_wkssvc.h new file mode 100644 index 0000000000..adc37c255b --- /dev/null +++ b/source4/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/source4/include/safe_string.h b/source4/include/safe_string.h new file mode 100644 index 0000000000..431dc400aa --- /dev/null +++ b/source4/include/safe_string.h @@ -0,0 +1,99 @@ +/* + Unix SMB/CIFS implementation. + Safe string 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. +*/ + +#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__; + +#endif /* !_SPLINT_ */ + +char * __unsafe_string_function_usage_here__(void); + +#if 0 && defined __GNUC__ && __GNUC__ >= 2 && defined __OPTIMIZE__ + +#define pstrcpy(d,s) ((sizeof(d) != sizeof(pstring) && sizeof(d) != sizeof(char *)) ? __unsafe_string_function_usage_here__() : safe_strcpy((d), (s),sizeof(pstring)-1)) +#define pstrcat(d,s) ((sizeof(d) != sizeof(pstring) && sizeof(d) != sizeof(char *)) ? __unsafe_string_function_usage_here__() : safe_strcat((d), (s),sizeof(pstring)-1)) +#define fstrcpy(d,s) ((sizeof(d) != sizeof(fstring) && sizeof(d) != sizeof(char *)) ? __unsafe_string_function_usage_here__() : safe_strcpy((d),(s),sizeof(fstring)-1)) +#define fstrcat(d,s) ((sizeof(d) != sizeof(fstring) && sizeof(d) != sizeof(char *)) ? __unsafe_string_function_usage_here__() : safe_strcat((d),(s),sizeof(fstring)-1)) + +#define fstrterminate(d) ((sizeof(d) != sizeof(fstring) && sizeof(d) != sizeof(char *)) ? __unsafe_string_function_usage_here__() : (((d)[sizeof(fstring)-1]) = '\0')) +#define pstrterminate(d) ((sizeof(d) != sizeof(pstring) && sizeof(d) != sizeof(char *)) ? __unsafe_string_function_usage_here__() : (((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))) + +#else + +#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 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)) + +#endif + +/* replace some string functions with multi-byte + versions */ +#define strlower(s) strlower_m(s) +#define strupper(s) strupper_m(s) + +/* 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) + +#define push_pstring_base(dest, src, pstring_base) \ + push_ascii(dest, src, sizeof(pstring)-PTR_DIFF(dest,pstring_base)-1, STR_TERMINATE) + +#endif diff --git a/source4/include/sam.h b/source4/include/sam.h new file mode 100644 index 0000000000..f46a6e7bcb --- /dev/null +++ b/source4/include/sam.h @@ -0,0 +1,238 @@ +/* + Unix SMB/CIFS implementation. + SAM structures + Copyright (C) Kai Krueger 2002 + Copyright (C) Stefan (metze) Metzmacher 2002 + Copyright (C) Simo Sorce 2002 + 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. +*/ + +#ifndef _SAM_H +#define _SAM_H + +/* We want to track down bugs early */ +#if 1 +#define SAM_ASSERT(x) SMB_ASSERT(x) +#else +#define SAM_ASSERT(x) while (0) { \ + if (!(x)) { + DEBUG(0, ("SAM_ASSERT failed!\n")) + return NT_STATUS_FAIL_CHECK;\ + } \ + } +#endif + + +/* let it be 0 until we have a stable interface --metze */ +#define SAM_INTERFACE_VERSION 0 + +/* use this inside a passdb module */ +#define SAM_MODULE_VERSIONING_MAGIC \ +int sam_version(void)\ +{\ + return SAM_INTERFACE_VERSION;\ +} + +/* Backend to use by default when no backend was specified */ +#define SAM_DEFAULT_BACKEND "plugin" + +typedef struct sam_domain_handle { + TALLOC_CTX *mem_ctx; + uint32 access_granted; + const struct sam_methods *current_sam_methods; /* sam_methods creating this handle */ + void (*free_fn)(struct sam_domain_handle **); + struct domain_data { + DOM_SID sid; /*SID of the domain. Should not be changed */ + char *name; /* Name of the domain */ + char *servername; /* */ + NTTIME max_passwordage; /* time till next password expiration */ + NTTIME min_passwordage; /* time till password can be changed again */ + NTTIME lockout_duration; /* time till login is allowed again after lockout*/ + NTTIME reset_count; /* time till bad login counter is reset */ + uint16 min_passwordlength; /* minimum number of characters for a password */ + uint16 password_history; /* number of passwords stored in history */ + uint16 lockout_count; /* number of bad login attempts before lockout */ + BOOL force_logoff; /* force logoff after logon hours have expired */ + BOOL login_pwdchange; /* Users need to logon to change their password */ + uint32 num_accounts; /* number of accounts in the domain */ + uint32 num_groups; /* number of global groups */ + uint32 num_aliases; /* number of local groups */ + uint32 sam_sequence_number; /* global sequence number */ + } private; +} SAM_DOMAIN_HANDLE; + +typedef struct sam_account_handle { + TALLOC_CTX *mem_ctx; + uint32 access_granted; + const struct sam_methods *current_sam_methods; /* sam_methods creating this handle */ + void (*free_fn)(struct sam_account_handle **); + struct sam_account_data { + uint32 init_flag; + 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 * account_name; /* account_name string */ + SAM_DOMAIN_HANDLE * domain; /* domain of account */ + char *full_name; /* account's full name string */ + char *unix_home_dir; /* UNIX home directory string */ + char *home_dir; /* home directory string */ + char *dir_drive; /* home directory drive string */ + char *logon_script; /* logon script string */ + char *profile_path; /* profile path string */ + char *acct_desc; /* account description string */ + char *workstations; /* login from workstations string */ + char *unknown_str; /* don't know what this is, yet. */ + char *munged_dial; /* munged path name and dial-back tel number */ + DOM_SID account_sid; /* Primary Account 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; /* if Null not available */ + uint16 acct_ctrl; /* account info (ACB_xxxx bit-mask) */ + uint32 unknown_1; /* 0x00ff ffff */ + uint16 logon_divs; /* 168 - number of hours in a week */ + uint32 hours_len; /* normally 21 bytes */ + uint8 hours[MAX_HOURS_LEN]; + uint32 unknown_2; /* 0x0002 0000 */ + uint32 unknown_3; /* 0x0000 04ec */ + } private; +} SAM_ACCOUNT_HANDLE; + +typedef struct sam_group_handle { + TALLOC_CTX *mem_ctx; + uint32 access_granted; + const struct sam_methods *current_sam_methods; /* sam_methods creating this handle */ + void (*free_fn)(struct sam_group_handle **); + struct sam_group_data { + char *group_name; + char *group_desc; + DOM_SID sid; + uint16 group_ctrl; /* specifies if the group is a local group or a global group */ + uint32 num_members; + } private; +} SAM_GROUP_HANDLE; + + +typedef struct sam_group_member { + DOM_SID sid; + BOOL group; /* specifies if it is a group or a account */ +} SAM_GROUP_MEMBER; + +typedef struct sam_account_enum { + DOM_SID sid; + char *account_name; + char *full_name; + char *account_desc; + uint16 acct_ctrl; +} SAM_ACCOUNT_ENUM; + +typedef struct sam_group_enum { + DOM_SID sid; + char *group_name; + char *group_desc; + uint16 group_ctrl; +} SAM_GROUP_ENUM; + + +/* bits for group_ctrl: to spezify if the group is global group or alias */ +#define GCB_LOCAL_GROUP 0x0001 +#define GCB_ALIAS_GROUP (GCB_LOCAL_GROUP |GCB_BUILTIN) +#define GCB_GLOBAL_GROUP 0x0002 +#define GCB_BUILTIN 0x1000 + +typedef struct sam_context +{ + struct sam_methods *methods; + TALLOC_CTX *mem_ctx; + + void (*free_fn)(struct sam_context **); +} SAM_CONTEXT; + +typedef struct sam_methods +{ + struct sam_context *parent; + struct sam_methods *next; + struct sam_methods *prev; + const char *backendname; + const char *domain_name; + DOM_SID domain_sid; + void *private_data; + + /* General API */ + + NTSTATUS (*sam_get_sec_desc) (const struct sam_methods *, const NT_USER_TOKEN *access_token, const DOM_SID *sid, SEC_DESC **sd); + NTSTATUS (*sam_set_sec_desc) (const struct sam_methods *, const NT_USER_TOKEN *access_token, const DOM_SID *sid, const SEC_DESC *sd); + + NTSTATUS (*sam_lookup_sid) (const struct sam_methods *, const NT_USER_TOKEN *access_token, TALLOC_CTX *mem_ctx, const DOM_SID *sid, char **name, uint32 *type); + NTSTATUS (*sam_lookup_name) (const struct sam_methods *, const NT_USER_TOKEN *access_token, const char *name, DOM_SID *sid, uint32 *type); + + /* Domain API */ + + NTSTATUS (*sam_update_domain) (const struct sam_methods *, const SAM_DOMAIN_HANDLE *domain); + NTSTATUS (*sam_get_domain_handle) (const struct sam_methods *, const NT_USER_TOKEN *access_token, uint32 access_desired, SAM_DOMAIN_HANDLE **domain); + + /* Account API */ + + NTSTATUS (*sam_create_account) (const struct sam_methods *, const NT_USER_TOKEN *access_token, uint32 access_desired, const char *account_name, uint16 acct_ctrl, SAM_ACCOUNT_HANDLE **account); + NTSTATUS (*sam_add_account) (const struct sam_methods *, const SAM_ACCOUNT_HANDLE *account); + NTSTATUS (*sam_update_account) (const struct sam_methods *, const SAM_ACCOUNT_HANDLE *account); + NTSTATUS (*sam_delete_account) (const struct sam_methods *, const SAM_ACCOUNT_HANDLE *account); + NTSTATUS (*sam_enum_accounts) (const struct sam_methods *, const NT_USER_TOKEN *access_token, uint16 acct_ctrl, uint32 *account_count, SAM_ACCOUNT_ENUM **accounts); + + NTSTATUS (*sam_get_account_by_sid) (const struct sam_methods *, const NT_USER_TOKEN *access_token, uint32 access_desired, const DOM_SID *accountsid, SAM_ACCOUNT_HANDLE **account); + NTSTATUS (*sam_get_account_by_name) (const struct sam_methods *, const NT_USER_TOKEN *access_token, uint32 access_desired, const char *name, SAM_ACCOUNT_HANDLE **account); + + /* Group API */ + + NTSTATUS (*sam_create_group) (const struct sam_methods *, const NT_USER_TOKEN *access_token, uint32 access_desired, const char *group_name, uint16 group_ctrl, SAM_GROUP_HANDLE **group); + NTSTATUS (*sam_add_group) (const struct sam_methods *, const SAM_GROUP_HANDLE *group); + NTSTATUS (*sam_update_group) (const struct sam_methods *, const SAM_GROUP_HANDLE *group); + NTSTATUS (*sam_delete_group) (const struct sam_methods *, const SAM_GROUP_HANDLE *group); + NTSTATUS (*sam_enum_groups) (const struct sam_methods *, const NT_USER_TOKEN *access_token, uint16 group_ctrl, uint32 *groups_count, SAM_GROUP_ENUM **groups); + NTSTATUS (*sam_get_group_by_sid) (const struct sam_methods *, const NT_USER_TOKEN *access_token, uint32 access_desired, const DOM_SID *groupsid, SAM_GROUP_HANDLE **group); + NTSTATUS (*sam_get_group_by_name) (const struct sam_methods *, const NT_USER_TOKEN *access_token, uint32 access_desired, const char *name, SAM_GROUP_HANDLE **group); + + NTSTATUS (*sam_add_member_to_group) (const struct sam_methods *, const SAM_GROUP_HANDLE *group, const SAM_GROUP_MEMBER *member); + NTSTATUS (*sam_delete_member_from_group) (const struct sam_methods *, const SAM_GROUP_HANDLE *group, const SAM_GROUP_MEMBER *member); + NTSTATUS (*sam_enum_groupmembers) (const struct sam_methods *, const SAM_GROUP_HANDLE *group, uint32 *members_count, SAM_GROUP_MEMBER **members); + + NTSTATUS (*sam_get_groups_of_sid) (const struct sam_methods *, const NT_USER_TOKEN *access_token, const DOM_SID **sids, uint16 group_ctrl, uint32 *group_count, SAM_GROUP_ENUM **groups); + + void (*free_private_data)(void **); +} SAM_METHODS; + +typedef NTSTATUS (*sam_init_function)(SAM_METHODS *, const char *); + +struct sam_init_function_entry { + char *module_name; + /* Function to create a member of the sam_methods list */ + sam_init_function init; +}; + +typedef struct sam_backend_entry { + char *module_name; + char *module_params; + char *domain_name; + DOM_SID *domain_sid; +} SAM_BACKEND_ENTRY; + + +#endif /* _SAM_H */ diff --git a/source4/include/secrets.h b/source4/include/secrets.h new file mode 100644 index 0000000000..183b29d7a8 --- /dev/null +++ b/source4/include/secrets.h @@ -0,0 +1,79 @@ +/* + * 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" + +/* 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" + +/* 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 + */ +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 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; + + +#endif /* _SECRETS_H */ diff --git a/source4/include/session.h b/source4/include/session.h new file mode 100644 index 0000000000..f613afee09 --- /dev/null +++ b/source4/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 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/source4/include/smb.h b/source4/include/smb.h new file mode 100644 index 0000000000..682d392c0f --- /dev/null +++ b/source4/include/smb.h @@ -0,0 +1,1363 @@ +/* + 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 + +#define NMB_PORT 137 +#define DGRAM_PORT 138 +#define SMB_PORT1 445 +#define SMB_PORT2 139 +#define SMB_PORTS "445 139" + +#define False (0) +#define True (1) +#define Auto (2) + +#ifndef _BOOL +typedef int BOOL; +#define _BOOL /* So we don't typedef BOOL again in vfs.h */ +#endif + +#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_NO_RANGE_CHECK 32 +#define STR_LEN8BIT 64 +#define STR_TERMINATE_ASCII 128 /* only terminate if ascii */ +#define STR_LEN_NOTERM 256 /* the length field is the unterminated length */ + +/* Debugging stuff */ +#include "debug.h" + +/* types of socket errors */ +enum socket_error {SOCKET_READ_TIMEOUT, + SOCKET_READ_EOF, + SOCKET_READ_ERROR, + SOCKET_WRITE_ERROR, + SOCKET_READ_BAD_SIG}; + +/* deny modes */ +#define DENY_DOS 0 +#define DENY_ALL 1 +#define DENY_WRITE 2 +#define DENY_READ 3 +#define DENY_NONE 4 +#define DENY_FCB 7 + +/* open modes */ +#define DOS_OPEN_RDONLY 0 +#define DOS_OPEN_WRONLY 1 +#define DOS_OPEN_RDWR 2 +#define DOS_OPEN_FCB 0xF + + +/**********************************/ +/* SMBopen field definitions */ +#define OPEN_FLAGS_DENY_MASK 0x70 +#define OPEN_FLAGS_DENY_DOS 0x00 +#define OPEN_FLAGS_DENY_ALL 0x10 +#define OPEN_FLAGS_DENY_WRITE 0x20 +#define OPEN_FLAGS_DENY_READ 0x30 +#define OPEN_FLAGS_DENY_NONE 0x40 + +#define OPEN_FLAGS_MODE_MASK 0x0F +#define OPEN_FLAGS_OPEN_READ 0 +#define OPEN_FLAGS_OPEN_WRITE 1 +#define OPEN_FLAGS_OPEN_RDWR 2 +#define OPEN_FLAGS_FCB 0xFF + + +/**********************************/ +/* SMBopenX field definitions */ + +/* OpenX Flags field. */ +#define OPENX_FLAGS_ADDITIONAL_INFO 0x01 +#define OPENX_FLAGS_REQUEST_OPLOCK 0x02 +#define OPENX_FLAGS_REQUEST_BATCH_OPLOCK 0x04 +#define OPENX_FLAGS_EA_LEN 0x08 +#define OPENX_FLAGS_EXTENDED_RETURN 0x10 + +/* desired access (open_mode), split info 4 4-bit nibbles */ +#define OPENX_MODE_ACCESS_MASK 0x000F +#define OPENX_MODE_ACCESS_READ 0x0000 +#define OPENX_MODE_ACCESS_WRITE 0x0001 +#define OPENX_MODE_ACCESS_RDWR 0x0002 +#define OPENX_MODE_ACCESS_EXEC 0x0003 +#define OPENX_MODE_ACCESS_FCB 0x000F + +#define OPENX_MODE_DENY_SHIFT 4 +#define OPENX_MODE_DENY_MASK (0xF << OPENX_MODE_DENY_SHIFT) +#define OPENX_MODE_DENY_DOS (DENY_DOS << OPENX_MODE_DENY_SHIFT) +#define OPENX_MODE_DENY_ALL (DENY_ALL << OPENX_MODE_DENY_SHIFT) +#define OPENX_MODE_DENY_WRITE (DENY_WRITE << OPENX_MODE_DENY_SHIFT) +#define OPENX_MODE_DENY_READ (DENY_READ << OPENX_MODE_DENY_SHIFT) +#define OPENX_MODE_DENY_NONE (DENY_NONE << OPENX_MODE_DENY_SHIFT) +#define OPENX_MODE_DENY_FCB (0xF << OPENX_MODE_DENY_SHIFT) + +#define OPENX_MODE_LOCALITY_MASK 0x0F00 /* what does this do? */ + +#define OPENX_MODE_NO_CACHE 0x1000 +#define OPENX_MODE_WRITE_THRU 0x4000 + +/* open function values */ +#define OPENX_OPEN_FUNC_MASK 0x3 +#define OPENX_OPEN_FUNC_FAIL 0x0 +#define OPENX_OPEN_FUNC_OPEN 0x1 +#define OPENX_OPEN_FUNC_TRUNC 0x2 + +/* The above can be OR'ed with... */ +#define OPENX_OPEN_FUNC_CREATE 0x10 + +/* openx action in reply */ +#define OPENX_ACTION_EXISTED 1 +#define OPENX_ACTION_CREATED 2 +#define OPENX_ACTION_TRUNCATED 3 + + +/**********************************/ +/* SMBntcreateX field definitions */ + +/* ntcreatex flags field. */ +#define NTCREATEX_FLAGS_REQUEST_OPLOCK 0x02 +#define NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK 0x04 +#define NTCREATEX_FLAGS_OPEN_DIRECTORY 0x08 +#define NTCREATEX_FLAGS_EXTENDED 0x10 + +/* the ntcreatex access_mask field + this is split into 4 pieces + AAAABBBBCCCCCCCCDDDDDDDDDDDDDDDD + A -> GENERIC_RIGHT_* + B -> SEC_RIGHT_* + C -> STD_RIGHT_* + D -> SA_RIGHT_* + + which set of SA_RIGHT_* bits is applicable depends on the type + of object. +*/ + + + +/* ntcreatex share_access field */ +#define NTCREATEX_SHARE_ACCESS_NONE 0 +#define NTCREATEX_SHARE_ACCESS_READ 1 +#define NTCREATEX_SHARE_ACCESS_WRITE 2 +#define NTCREATEX_SHARE_ACCESS_DELETE 4 + +/* ntcreatex open_disposition field */ +#define NTCREATEX_DISP_SUPERSEDE 0 /* supersede existing file (if it exists) */ +#define NTCREATEX_DISP_OPEN 1 /* if file exists open it, else fail */ +#define NTCREATEX_DISP_CREATE 2 /* if file exists fail, else create it */ +#define NTCREATEX_DISP_OPEN_IF 3 /* if file exists open it, else create it */ +#define NTCREATEX_DISP_OVERWRITE 4 /* if exists overwrite, else fail */ +#define NTCREATEX_DISP_OVERWRITE_IF 5 /* if exists overwrite, else create */ + +/* ntcreatex create_options field */ +#define NTCREATEX_OPTIONS_DIRECTORY 0x0001 +#define NTCREATEX_OPTIONS_WRITE_THROUGH 0x0002 +#define NTCREATEX_OPTIONS_SEQUENTIAL_ONLY 0x0004 +#define NTCREATEX_OPTIONS_SYNC_ALERT 0x0010 +#define NTCREATEX_OPTIONS_ASYNC_ALERT 0x0020 +#define NTCREATEX_OPTIONS_NON_DIRECTORY_FILE 0x0040 +#define NTCREATEX_OPTIONS_NO_EA_KNOWLEDGE 0x0200 +#define NTCREATEX_OPTIONS_EIGHT_DOT_THREE_ONLY 0x0400 +#define NTCREATEX_OPTIONS_RANDOM_ACCESS 0x0800 +#define NTCREATEX_OPTIONS_DELETE_ON_CLOSE 0x1000 +#define NTCREATEX_OPTIONS_OPEN_BY_FILE_ID 0x2000 + +/* ntcreatex impersonation field */ +#define NTCREATEX_IMPERSONATION_ANONYMOUS 0 +#define NTCREATEX_IMPERSONATION_IDENTIFICATION 1 +#define NTCREATEX_IMPERSONATION_IMPERSONATION 2 +#define NTCREATEX_IMPERSONATION_DELEGATION 3 + +/* ntcreatex security flags bit field */ +#define NTCREATEX_SECURITY_DYNAMIC 1 +#define NTCREATEX_SECURITY_ALL 2 + +/* ntcreatex create_action in reply */ +#define NTCREATEX_ACTION_EXISTED 1 +#define NTCREATEX_ACTION_CREATED 2 +#define NTCREATEX_ACTION_TRUNCATED 3 +/* the value 5 can also be returned when you try to create a directory with + incorrect parameters - what does it mean? maybe created temporary file? */ +#define NTCREATEX_ACTION_UNKNOWN 5 + + + +/* 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" + +/* this is a trade with jeremy - I agreed to use uint_t instead of + * bare unsigned if he agreed to not use non-braced if statements + * (13/4/2003 - train to gottenginen) */ +typedef unsigned int uint_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]; + +/* This error code can go into the client smb_rw_error. */ +#define WRITE_ERROR 4 + +#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 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_MAX_PIPES 9 + +/* 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 + +#ifndef MAXSUBAUTHS +#define MAXSUBAUTHS 15 /* max sub authorities in a SID */ +#endif + +/* SID Types */ +enum SID_NAME_USE +{ + SID_NAME_USE_NONE = 0,/* NOTUSED */ + SID_NAME_USER = 1, /* user */ + SID_NAME_DOM_GRP = 2, /* domain group */ + SID_NAME_DOMAIN = 3, /* domain: don't know what this is */ + SID_NAME_ALIAS = 4, /* local group */ + SID_NAME_WKN_GRP = 5, /* well-known group */ + SID_NAME_DELETED = 6, /* deleted account: needed for c2 rating */ + SID_NAME_INVALID = 7, /* invalid account */ + SID_NAME_UNKNOWN = 8 /* oops. */ +}; + +/** + * @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; + +/* 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 { + unsigned int entries; + uint16 list[VUID_CACHE_SIZE]; +}; + +/* Include VFS stuff */ + +#include "smb_acls.h" +#include "enums.h" +#include "events.h" +#include "context.h" +#include "smb_interfaces.h" +#include "ntvfs.h" + +typedef struct smb_vfs_handle_struct +{ + void *data; + /* Handle on dlopen() call */ + void *handle; + struct smb_vfs_handle_struct *next, *prev; + +} smb_vfs_handle_struct; + +struct tcon_context { + struct tcon_context *next, *prev; + + /* the server context that this was created on */ + struct server_context *smb; + + /* a talloc context for all data in this structure */ + TALLOC_CTX *mem_ctx; + + /* a private structure used by the active NTVFS backend */ + void *ntvfs_private; + + uint16 cnum; /* an index passed over the wire (the TID) */ + int service; + enum ntvfs_type type; + BOOL read_only; + BOOL admin_user; + + /* the NTVFS operations - see source/ntvfs/ and include/ntvfs.h for details */ + struct ntvfs_ops *ntvfs_ops; + + /* the reported filesystem type */ + char *fs_type; + + /* the reported device type */ + char *dev_type; +}; + +struct current_user +{ + struct tcon_context *conn; + uint16 vuid; + uid_t uid; + gid_t gid; + int ngroups; + gid_t *groups; + NT_USER_TOKEN *nt_user_token; +}; + +/* 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 userdom_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; + +/* 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 + +/* + * bit flags representing initialized fields in SAM_ACCOUNT + */ +enum pdb_elements { + PDB_UNINIT, + PDB_UID, + PDB_GID, + PDB_SMBHOME, + PDB_PROFILE, + PDB_DRIVE, + PDB_LOGONSCRIPT, + PDB_LOGONTIME, + PDB_LOGOFFTIME, + PDB_KICKOFFTIME, + 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_UNKNOWN3, + PDB_UNKNOWN5, + PDB_UNKNOWN6, + PDB_LMPASSWD, + PDB_NTPASSWD, + + /* this must be the last element */ + PDB_COUNT, +}; + +enum pdb_value_state { + PDB_DEFAULT=0, + PDB_SET, + PDB_CHANGED +}; + +#define IS_SAM_UNIX_USER(x) \ + (( pdb_get_init_flags(x, PDB_UID) != PDB_DEFAULT ) \ + && ( pdb_get_init_flags(x,PDB_GID) != PDB_DEFAULT )) + +#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) + +typedef struct sam_passwd +{ + TALLOC_CTX *mem_ctx; + + void (*free_fn)(struct sam_passwd **); + + struct pdb_methods *methods; + + struct user_data { + /* initiailization 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 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 */ + + uid_t uid; /* this is a unix uid_t */ + gid_t gid; /* this is a unix gid_t */ + 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 unknown_3; /* 0x00ff ffff */ + + uint16 logon_divs; /* 168 - number of hours in a week */ + uint32 hours_len; /* normally 21 bytes */ + uint8 hours[MAX_HOURS_LEN]; + + uint32 unknown_5; /* 0x0002 0000 */ + uint32 unknown_6; /* 0x0000 04ec */ + } private; + + /* Lets see if the remaining code can get the hint that you + are meant to use the pdb_...() functions. */ + +} SAM_ACCOUNT; + +/* + * 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; +}; + +/* 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_ENUM,P_SEP +} parm_type; + +typedef enum +{ + P_LOCAL,P_GLOBAL,P_SEPARATOR,P_NONE +} parm_class; + +struct enum_list { + int value; + const char *name; +}; + +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; +}; + +#define FLAG_BASIC 0x0001 /* fundamental options */ +#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 the wizard will operate on */ +#define FLAG_DEVELOPER 0x0040 /* Parameters that the wizard will operate on */ +#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. */ +#define FLAG_CMDLINE 0x8000 /* this option was set from the command line */ + +#ifndef LOCKING_VERSION +#define LOCKING_VERSION 4 +#endif /* LOCKING_VERSION */ + + +/* the basic packet size, assuming no words or bytes. Does not include the NBT header */ +#define MIN_SMB_SIZE 35 + +/* when using NBT encapsulation every packet has a 4 byte header */ +#define NBT_HDR_SIZE 4 + +/* offsets into message header for common items - NOTE: These have + changed from being offsets from the base of the NBT packet to the base of the SMB packet. + this has reduced all these values by 4 +*/ +#define HDR_COM 4 +#define HDR_RCLS 5 +#define HDR_REH 6 +#define HDR_ERR 7 +#define HDR_FLG 9 +#define HDR_FLG2 10 +#define HDR_PIDHIGH 12 +#define HDR_SS_FIELD 14 +#define HDR_TID 24 +#define HDR_PID 26 +#define HDR_UID 28 +#define HDR_MID 30 +#define HDR_WCT 32 +#define HDR_VWV 33 + + +/* types of buffers in core SMB protocol */ +#define SMB_DATA_BLOCK 0x1 +#define SMB_ASCII4 0x4 + + +/* flag defines. CIFS spec 3.1.1 */ +#define FLAG_SUPPORT_LOCKREAD 0x01 +#define FLAG_CLIENT_BUF_AVAIL 0x02 +#define FLAG_RESERVED 0x04 +#define FLAG_CASELESS_PATHNAMES 0x08 +#define FLAG_CANONICAL_PATHNAMES 0x10 +#define FLAG_REQUEST_OPLOCK 0x20 +#define FLAG_REQUEST_BATCH_OPLOCK 0x40 +#define FLAG_REPLY 0x80 + +/* the complete */ +#define SMBmkdir 0x00 /* create directory */ +#define SMBrmdir 0x01 /* delete directory */ +#define SMBopen 0x02 /* open file */ +#define SMBcreate 0x03 /* create file */ +#define SMBclose 0x04 /* close file */ +#define SMBflush 0x05 /* flush file */ +#define SMBunlink 0x06 /* delete file */ +#define SMBmv 0x07 /* rename file */ +#define SMBgetatr 0x08 /* get file attributes */ +#define SMBsetatr 0x09 /* set file attributes */ +#define SMBread 0x0A /* read from file */ +#define SMBwrite 0x0B /* write to file */ +#define SMBlock 0x0C /* lock byte range */ +#define SMBunlock 0x0D /* unlock byte range */ +#define SMBctemp 0x0E /* create temporary file */ +#define SMBmknew 0x0F /* make new file */ +#define SMBchkpth 0x10 /* check directory path */ +#define SMBexit 0x11 /* process exit */ +#define SMBlseek 0x12 /* seek */ +#define SMBtcon 0x70 /* tree connect */ +#define SMBtconX 0x75 /* tree connect and X*/ +#define SMBtdis 0x71 /* tree disconnect */ +#define SMBnegprot 0x72 /* negotiate protocol */ +#define SMBdskattr 0x80 /* get disk attributes */ +#define SMBsearch 0x81 /* search directory */ +#define SMBsplopen 0xC0 /* open print spool file */ +#define SMBsplwr 0xC1 /* write to print spool file */ +#define SMBsplclose 0xC2 /* close print spool file */ +#define SMBsplretq 0xC3 /* return print queue */ +#define SMBsends 0xD0 /* send single block message */ +#define SMBsendb 0xD1 /* send broadcast message */ +#define SMBfwdname 0xD2 /* forward user name */ +#define SMBcancelf 0xD3 /* cancel forward */ +#define SMBgetmac 0xD4 /* get machine name */ +#define SMBsendstrt 0xD5 /* send start of multi-block message */ +#define SMBsendend 0xD6 /* send end of multi-block message */ +#define SMBsendtxt 0xD7 /* send text of multi-block message */ + +/* Core+ protocol */ +#define SMBlockread 0x13 /* Lock a range and read */ +#define SMBwriteunlock 0x14 /* write then range then unlock it */ +#define SMBreadbraw 0x1a /* read a block of data with no smb header */ +#define SMBwritebraw 0x1d /* write a block of data with no smb header */ +#define SMBwritec 0x20 /* secondary write request */ +#define SMBwriteclose 0x2c /* write a file then close it */ + +/* dos extended protocol */ +#define SMBreadBraw 0x1A /* read block raw */ +#define SMBreadBmpx 0x1B /* read block multiplexed */ +#define SMBreadBs 0x1C /* read block (secondary response) */ +#define SMBwriteBraw 0x1D /* write block raw */ +#define SMBwriteBmpx 0x1E /* write block multiplexed */ +#define SMBwriteBs 0x1F /* write block (secondary request) */ +#define SMBwriteC 0x20 /* write complete response */ +#define SMBsetattrE 0x22 /* set file attributes expanded */ +#define SMBgetattrE 0x23 /* get file attributes expanded */ +#define SMBlockingX 0x24 /* lock/unlock byte ranges and X */ +#define SMBtrans 0x25 /* transaction - name, bytes in/out */ +#define SMBtranss 0x26 /* transaction (secondary request/response) */ +#define SMBioctl 0x27 /* IOCTL */ +#define SMBioctls 0x28 /* IOCTL (secondary request/response) */ +#define SMBcopy 0x29 /* copy */ +#define SMBmove 0x2A /* move */ +#define SMBecho 0x2B /* echo */ +#define SMBopenX 0x2D /* open and X */ +#define SMBreadX 0x2E /* read and X */ +#define SMBwriteX 0x2F /* write and X */ +#define SMBsesssetupX 0x73 /* Session Set Up & X (including User Logon) */ +#define SMBffirst 0x82 /* find first */ +#define SMBfunique 0x83 /* find unique */ +#define SMBfclose 0x84 /* find close */ +#define SMBkeepalive 0x85 /* keepalive */ +#define SMBinvalid 0xFE /* invalid command */ + +/* Extended 2.0 protocol */ +#define SMBtrans2 0x32 /* TRANS2 protocol set */ +#define SMBtranss2 0x33 /* TRANS2 protocol set, secondary command */ +#define SMBfindclose 0x34 /* Terminate a TRANSACT2_FINDFIRST */ +#define SMBfindnclose 0x35 /* Terminate a TRANSACT2_FINDNOTIFYFIRST */ +#define SMBulogoffX 0x74 /* user logoff */ + +/* NT SMB extensions. */ +#define SMBnttrans 0xA0 /* NT transact */ +#define SMBnttranss 0xA1 /* NT transact secondary */ +#define SMBntcreateX 0xA2 /* NT create and X */ +#define SMBntcancel 0xA4 /* NT cancel */ + +/* used to indicate end of chain */ +#define SMB_CHAIN_NONE 0xFF + +/* These are the trans subcommands */ +#define TRANSACT_SETNAMEDPIPEHANDLESTATE 0x01 +#define TRANSACT_DCERPCCMD 0x26 +#define TRANSACT_WAITNAMEDPIPEHANDLESTATE 0x53 + +/* These are the NT transact sub commands. */ +#define NT_TRANSACT_CREATE 1 +#define NT_TRANSACT_IOCTL 2 +#define NT_TRANSACT_SET_SECURITY_DESC 3 +#define NT_TRANSACT_NOTIFY_CHANGE 4 +#define NT_TRANSACT_RENAME 5 +#define NT_TRANSACT_QUERY_SECURITY_DESC 6 + +/* this is used on a TConX. I'm not sure the name is very helpful though */ +#define SMB_SUPPORT_SEARCH_BITS 0x0001 +#define SMB_SHARE_IN_DFS 0x0002 + +/* Named pipe write mode flags. Used in writeX calls. */ +#define PIPE_RAW_MODE 0x4 +#define PIPE_START_MESSAGE 0x8 + +/* the desired access to use when opening a pipe */ +#define DESIRED_ACCESS_PIPE 0x2019f + + +/* Mapping of generic access rights for files to specific rights. */ +#define FILE_GENERIC_ALL (STANDARD_RIGHTS_REQUIRED_ACCESS| NT_ACCESS_SYNCHRONIZE_ACCESS|FILE_ALL_ACCESS) + +#define FILE_GENERIC_READ (STANDARD_RIGHTS_READ_ACCESS|FILE_READ_DATA|FILE_READ_ATTRIBUTES|\ + FILE_READ_EA|NT_ACCESS_SYNCHRONIZE_ACCESS) + +#define FILE_GENERIC_WRITE (STANDARD_RIGHTS_WRITE_ACCESS|FILE_WRITE_DATA|FILE_WRITE_ATTRIBUTES|\ + FILE_WRITE_EA|FILE_APPEND_DATA|NT_ACCESS_SYNCHRONIZE_ACCESS) + +#define FILE_GENERIC_EXECUTE (STANDARD_RIGHTS_EXECUTE_ACCESS|FILE_READ_ATTRIBUTES|\ + FILE_EXECUTE|NT_ACCESS_SYNCHRONIZE_ACCESS) + + +/* FileAttributes (search attributes) field */ +#define FILE_ATTRIBUTE_READONLY 0x0001 +#define FILE_ATTRIBUTE_HIDDEN 0x0002 +#define FILE_ATTRIBUTE_SYSTEM 0x0004 +#define FILE_ATTRIBUTE_VOLUME 0x0008 +#define FILE_ATTRIBUTE_DIRECTORY 0x0010 +#define FILE_ATTRIBUTE_ARCHIVE 0x0020 +#define FILE_ATTRIBUTE_DEVICE 0x0040 +#define FILE_ATTRIBUTE_NORMAL 0x0080 +#define FILE_ATTRIBUTE_TEMPORARY 0x0100 +#define FILE_ATTRIBUTE_SPARSE 0x0200 +#define FILE_ATTRIBUTE_REPARSE_POINT 0x0400 +#define FILE_ATTRIBUTE_COMPRESSED 0x0800 +#define FILE_ATTRIBUTE_OFFLINE 0x1000 +#define FILE_ATTRIBUTE_NONINDEXED 0x2000 +#define FILE_ATTRIBUTE_ENCRYPTED 0x4000 + +/* 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 + +/* 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 + +/* Filesystem Attributes. */ +#define FILE_CASE_SENSITIVE_SEARCH 0x01 +#define FILE_CASE_PRESERVED_NAMES 0x02 +#define FILE_UNICODE_ON_DISK 0x04 +/* According to cifs9f, this is 4, not 8 */ +/* Acconding to testing, this actually sets the security attribute! */ +#define FILE_PERSISTENT_ACLS 0x08 +/* These entries added from cifs9f --tsb */ +#define FILE_FILE_COMPRESSION 0x10 +#define FILE_VOLUME_QUOTAS 0x20 +/* I think this is wrong. JRA #define FILE_DEVICE_IS_MOUNTED 0x20 */ +#define FILE_VOLUME_SPARSE_FILE 0x40 +#define FILE_VOLUME_IS_COMPRESSED 0x8000 + +/* ChangeNotify flags. */ +#define FILE_NOTIFY_CHANGE_FILE 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 + +/* change notify action results */ +#define NOTIFY_ACTION_ADDED 1 +#define NOTIFY_ACTION_REMOVED 2 +#define NOTIFY_ACTION_MODIFIED 3 +#define NOTIFY_ACTION_OLD_NAME 4 +#define NOTIFY_ACTION_NEW_NAME 5 +#define NOTIFY_ACTION_ADDED_STREAM 6 +#define NOTIFY_ACTION_REMOVED_STREAM 7 +#define NOTIFY_ACTION_MODIFIED_STREAM 8 + +/* seek modes for smb_seek */ +#define SEEK_MODE_START 0 +#define SEEK_MODE_CURRENT 1 +#define SEEK_MODE_END 2 + +/* where to find the base of the SMB packet proper */ +/* REWRITE TODO: smb_base needs to be removed */ +#define smb_base(buf) (((char *)(buf))+4) + +/* we don't allow server strings to be longer than 48 characters as + otherwise NT will not honour the announce packets */ +#define MAX_SERVER_STRING_LENGTH 48 + + +#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 + +/* + * 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 */ + +/* 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 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 + +/* 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)) + + +#define CMD_REPLY 0x8000 + +#include "smb_macros.h" + +/* A netbios name structure. */ +struct nmb_name { + char name[17]; + char scope[64]; + unsigned int name_type; +}; + + +/* A netbios node status array element. */ +struct node_status { + char name[16]; + unsigned char type; + unsigned char flags; +}; + +struct pwd_info +{ + BOOL null_pwd; + BOOL cleartext; + BOOL crypted; + + fstring password; + + uchar smb_lm_pwd[16]; + uchar smb_nt_pwd[16]; + + uchar smb_lm_owf[24]; + uchar smb_nt_owf[128]; + size_t nt_owf_len; + + uchar lm_cli_chal[8]; + uchar nt_cli_chal[128]; + size_t nt_cli_chal_len; + + uchar sess_key[16]; +}; + +#include "rpc_creds.h" +#include "rpc_misc.h" +#include "rpc_secdes.h" +#include "nt_printing.h" + +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; + + uint8 session_key[16]; + + 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, const char **inbuf, size_t *inbytesleft, + char **outbuf, size_t *outbytesleft); + size_t (*pull)(void *cd, const char **inbuf, size_t *inbytesleft, + char **outbuf, size_t *outbytesleft); + size_t (*push)(void *cd, const 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 + +/* Module support */ +typedef int (init_module_function) (void); + + +/* a set of flags to control handling of request structures */ +#define REQ_CONTROL_PROTECTED (1<<0) /* don't destroy this request */ +#define REQ_CONTROL_LARGE (1<<1) /* allow replies larger than max_xmit */ +#define REQ_CONTROL_ASYNC (1<<2) /* the backend will answer this one later */ + +/* passed to br lock code */ +enum brl_type {READ_LOCK, WRITE_LOCK, PENDING_LOCK}; + +#include "popt_common.h" + +#endif /* _SMB_H */ diff --git a/source4/include/smb_acls.h b/source4/include/smb_acls.h new file mode 100644 index 0000000000..e7edb62bde --- /dev/null +++ b/source4/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 for UnixWare / OpenUNIX. + * Modified by Toomas Soome 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 + +/* 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 "/usr/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/source4/include/smb_interfaces.h b/source4/include/smb_interfaces.h new file mode 100644 index 0000000000..70cff11395 --- /dev/null +++ b/source4/include/smb_interfaces.h @@ -0,0 +1,1898 @@ +/* + Unix SMB/CIFS implementation. + SMB request interface structures + 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. +*/ + + +typedef SMB_BIG_UINT large_t; + +/* Globally Unique ID */ +#define GUID_SIZE 16 +typedef struct guid_info +{ + uint8 info[GUID_SIZE]; +} GUID; + +/* 64 bit time (100usec) 1601 - cifs6.txt, section 3.5, page 30 */ +typedef struct nttime_info +{ + uint32 low; + uint32 high; +} NTTIME; + + +/* this structure is just a wrapper for a string, the only reason we + bother with this is that it allows us to check the length provided + on the wire in testsuite test code to ensure that we are + terminating names in the same way that win2003 is. The *ONLY* time + you should ever look at the 'private_length' field in this + structure is inside compliance test code, in all cases just use the + null terminated char* as the definitive definition of the + string + + also note that this structure is only used in packets where there + is an explicit length provided on the wire (hence the name). That + length is placed in 'private_length'. For packets where the length + is always determined by NULL or packet termination a normal char* + is used. + */ +typedef struct { + uint32 private_length; + const char *s; +} WIRE_STRING; + + +/* + this header defines the structures and unions used between the SMB + parser and the backends. +*/ + +/* struct used for SMBlseek call */ +struct smb_seek { + struct { + uint16 fnum; + uint16 mode; + int32 offset; /* signed */ + } in; + struct { + uint32 offset; + } out; +}; + + +/* struct used in unlink() call */ +struct smb_unlink { + struct { + const char *pattern; + uint16 attrib; + } in; +}; + + +/* struct used in chkpath() call */ +struct smb_chkpath { + struct { + const char *path; + } in; +}; + +enum mkdir_level {RAW_MKDIR_GENERIC, RAW_MKDIR_MKDIR, RAW_MKDIR_T2MKDIR}; + +/* union used in mkdir() call */ +union smb_mkdir { + /* generic level */ + struct { + enum mkdir_level level; + } generic; + + struct { + enum mkdir_level level; + struct { + const char *path; + } in; + } mkdir; + + struct { + enum mkdir_level level; + struct { + const char *path; + uint_t num_eas; + struct ea_struct *eas; + } in; + } t2mkdir; +}; + +/* struct used in rmdir() call */ +struct smb_rmdir { + struct { + const char *path; + } in; +}; + +/* struct used in rename() call */ +struct smb_rename { + struct { + const char *pattern1; + const char *pattern2; + uint16 attrib; + } in; +}; + +enum tcon_level {RAW_TCON_TCON, RAW_TCON_TCONX}; + +/* union used in tree connect call */ +union smb_tcon { + /* generic interface */ + struct { + enum tcon_level level; + } generic; + + /* SMBtcon interface */ + struct { + enum tcon_level level; + + struct { + const char *service; + const char *password; + const char *dev; + } in; + struct { + uint16 max_xmit; + uint16 cnum; + } out; + } tcon; + + /* SMBtconX interface */ + struct { + enum tcon_level level; + + struct { + uint16 flags; + DATA_BLOB password; + const char *path; + const char *device; + } in; + struct { + uint16 options; + char *dev_type; + char *fs_type; + uint16 cnum; + } out; + } tconx; +}; + + +enum sesssetup_level {RAW_SESSSETUP_GENERIC, RAW_SESSSETUP_OLD, RAW_SESSSETUP_NT1, RAW_SESSSETUP_SPNEGO}; + +/* union used in session_setup call */ +union smb_sesssetup { + + /* generic interface - used for auto selecting based on negotiated + protocol options */ + struct { + enum sesssetup_level level; + + struct { + uint32 sesskey; + uint32 capabilities; + const char *password; + const char *user; + const char *domain; + } in; + struct { + uint16 vuid; + char *os; + char *lanman; + char *domain; + } out; + } generic; + + /* the pre-NT1 interface */ + struct { + enum sesssetup_level level; + + struct { + uint16 bufsize; + uint16 mpx_max; + uint16 vc_num; + uint32 sesskey; + DATA_BLOB password; + const char *user; + const char *domain; + const char *os; + const char *lanman; + } in; + struct { + uint16 action; + uint16 vuid; + char *os; + char *lanman; + char *domain; + } out; + } old; + + /* the NT1 interface */ + struct { + enum sesssetup_level level; + + struct { + uint16 bufsize; + uint16 mpx_max; + uint16 vc_num; + uint32 sesskey; + uint32 capabilities; + DATA_BLOB password1; + DATA_BLOB password2; + const char *user; + const char *domain; + const char *os; + const char *lanman; + } in; + struct { + uint16 action; + uint16 vuid; + char *os; + char *lanman; + char *domain; + } out; + } nt1; + + + /* the SPNEGO interface */ + struct { + enum sesssetup_level level; + + struct { + uint16 bufsize; + uint16 mpx_max; + uint16 vc_num; + uint32 sesskey; + uint32 capabilities; + DATA_BLOB secblob; + const char *os; + const char *lanman; + const char *domain; + } in; + struct { + uint16 action; + DATA_BLOB secblob; + char *os; + char *lanman; + char *domain; + uint16 vuid; + } out; + } spnego; +}; + +/* Note that the specified enum values are identical to the actual info-levels used + * on the wire. + */ +enum fileinfo_level {RAW_FILEINFO_GENERIC = 0xF000, + RAW_FILEINFO_GETATTR, /* SMBgetatr */ + RAW_FILEINFO_GETATTRE, /* SMBgetattrE */ + RAW_FILEINFO_STANDARD = SMB_QFILEINFO_STANDARD, + RAW_FILEINFO_EA_SIZE = SMB_QFILEINFO_EA_SIZE, + RAW_FILEINFO_ALL_EAS = SMB_QFILEINFO_ALL_EAS, + RAW_FILEINFO_IS_NAME_VALID = SMB_QFILEINFO_IS_NAME_VALID, + RAW_FILEINFO_BASIC_INFO = SMB_QFILEINFO_BASIC_INFO, + RAW_FILEINFO_STANDARD_INFO = SMB_QFILEINFO_STANDARD_INFO, + RAW_FILEINFO_EA_INFO = SMB_QFILEINFO_EA_INFO, + RAW_FILEINFO_NAME_INFO = SMB_QFILEINFO_NAME_INFO, + RAW_FILEINFO_ALL_INFO = SMB_QFILEINFO_ALL_INFO, + RAW_FILEINFO_ALT_NAME_INFO = SMB_QFILEINFO_ALT_NAME_INFO, + RAW_FILEINFO_STREAM_INFO = SMB_QFILEINFO_STREAM_INFO, + RAW_FILEINFO_COMPRESSION_INFO = SMB_QFILEINFO_COMPRESSION_INFO, + RAW_FILEINFO_UNIX_BASIC = SMB_QFILEINFO_UNIX_BASIC, + RAW_FILEINFO_UNIX_LINK = SMB_QFILEINFO_UNIX_LINK, + RAW_FILEINFO_BASIC_INFORMATION = SMB_QFILEINFO_BASIC_INFORMATION, + RAW_FILEINFO_STANDARD_INFORMATION = SMB_QFILEINFO_STANDARD_INFORMATION, + RAW_FILEINFO_INTERNAL_INFORMATION = SMB_QFILEINFO_INTERNAL_INFORMATION, + RAW_FILEINFO_EA_INFORMATION = SMB_QFILEINFO_EA_INFORMATION, + RAW_FILEINFO_ACCESS_INFORMATION = SMB_QFILEINFO_ACCESS_INFORMATION, + RAW_FILEINFO_NAME_INFORMATION = SMB_QFILEINFO_NAME_INFORMATION, + RAW_FILEINFO_POSITION_INFORMATION = SMB_QFILEINFO_POSITION_INFORMATION, + RAW_FILEINFO_MODE_INFORMATION = SMB_QFILEINFO_MODE_INFORMATION, + RAW_FILEINFO_ALIGNMENT_INFORMATION = SMB_QFILEINFO_ALIGNMENT_INFORMATION, + RAW_FILEINFO_ALL_INFORMATION = SMB_QFILEINFO_ALL_INFORMATION, + RAW_FILEINFO_ALT_NAME_INFORMATION = SMB_QFILEINFO_ALT_NAME_INFORMATION, + RAW_FILEINFO_STREAM_INFORMATION = SMB_QFILEINFO_STREAM_INFORMATION, + RAW_FILEINFO_COMPRESSION_INFORMATION = SMB_QFILEINFO_COMPRESSION_INFORMATION, + RAW_FILEINFO_NETWORK_OPEN_INFORMATION = SMB_QFILEINFO_NETWORK_OPEN_INFORMATION, + RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION = SMB_QFILEINFO_ATTRIBUTE_TAG_INFORMATION +}; + + +/* union used in qfileinfo() and qpathinfo() backend calls */ +union smb_fileinfo { + /* generic interface: + * matches RAW_FILEINFO_GENERIC */ + struct { + enum fileinfo_level level; + + /* each level can be called on either a pathname or a + * filename, in either case the return format is + * identical */ + union smb_fileinfo_in { + const char *fname; + uint16 fnum; + } in; + + struct { + uint16 attrib; + uint32 ea_size; + uint_t num_eas; + struct ea_struct { + uint8 flags; + WIRE_STRING name; + DATA_BLOB value; + } *eas; + NTTIME create_time; + NTTIME access_time; + NTTIME write_time; + NTTIME change_time; + uint32 ex_attrib; + large_t alloc_size; + large_t size; + uint32 nlink; + WIRE_STRING fname; + WIRE_STRING alt_fname; + uint8 delete_pending; + uint8 directory; + large_t compressed_size; + uint16 format; + uint8 unit_shift; + uint8 chunk_shift; + uint8 cluster_shift; + uint32 device; + uint32 inode; + uint32 access_flags; /* seen 0x001f01ff from w2k3 */ + large_t position; + uint32 mode; + uint32 alignment_requirement; + uint32 reparse_tag; + uint_t num_streams; + struct stream_struct { + large_t size; + large_t alloc_size; + WIRE_STRING stream_name; + } *streams; + } out; + } generic; + + + /* SMBgetatr interface: + * matches RAW_FILEINFO_GETATTR */ + struct { + enum fileinfo_level level; + union smb_fileinfo_in in; + + struct { + uint16 attrib; + uint32 size; + time_t write_time; + } out; + } getattr; + + /* SMBgetattrE interface */ + struct { + enum fileinfo_level level; + union smb_fileinfo_in in; + + struct { + time_t create_time; + time_t access_time; + time_t write_time; + uint32 size; + uint32 alloc_size; + uint16 attrib; + } out; + } getattre; + + /* trans2 RAW_FILEINFO_STANDARD interface */ + struct { + enum fileinfo_level level; + union smb_fileinfo_in in; + + struct { + time_t create_time; + time_t access_time; + time_t write_time; + uint32 size; + uint32 alloc_size; + uint16 attrib; + } out; + } standard; + + /* trans2 RAW_FILEINFO_EA_SIZE interface */ + struct { + enum fileinfo_level level; + union smb_fileinfo_in in; + + struct { + time_t create_time; + time_t access_time; + time_t write_time; + uint32 size; + uint32 alloc_size; + uint16 attrib; + uint32 ea_size; + } out; + } ea_size; + + /* trans2 RAW_FILEINFO_ALL_EAS interface */ + struct { + enum fileinfo_level level; + union smb_fileinfo_in in; + + struct { + /* the ea_size is implied by the list */ + uint_t num_eas; + struct ea_struct *eas; + } out; + } all_eas; + + /* trans2 qpathinfo RAW_FILEINFO_IS_NAME_VALID interface + only valid for a QPATHNAME call - no returned data */ + struct { + enum fileinfo_level level; + union smb_fileinfo_in in; + } is_name_valid; + + /* RAW_FILEINFO_BASIC_INFO and RAW_FILEINFO_BASIC_INFORMATION interfaces */ + struct { + enum fileinfo_level level; + union smb_fileinfo_in in; + + struct { + NTTIME create_time; + NTTIME access_time; + NTTIME write_time; + NTTIME change_time; + uint32 attrib; + } out; + } basic_info; + + + /* RAW_FILEINFO_STANDARD_INFO and RAW_FILEINFO_STANDARD_INFORMATION interfaces */ + struct { + enum fileinfo_level level; + union smb_fileinfo_in in; + + struct { + large_t alloc_size; + large_t size; + uint32 nlink; + BOOL delete_pending; + BOOL directory; + } out; + } standard_info; + + /* RAW_FILEINFO_EA_INFO and RAW_FILEINFO_EA_INFORMATION interfaces */ + struct { + enum fileinfo_level level; + union smb_fileinfo_in in; + + struct { + uint32 ea_size; + } out; + } ea_info; + + /* RAW_FILEINFO_NAME_INFO and RAW_FILEINFO_NAME_INFORMATION interfaces */ + struct { + enum fileinfo_level level; + union smb_fileinfo_in in; + + struct { + WIRE_STRING fname; + } out; + } name_info; + + /* RAW_FILEINFO_ALL_INFO and RAW_FILEINFO_ALL_INFORMATION interfaces */ + struct { + enum fileinfo_level level; + union smb_fileinfo_in in; + + struct { + NTTIME create_time; + NTTIME access_time; + NTTIME write_time; + NTTIME change_time; + uint32 attrib; + large_t alloc_size; + large_t size; + uint32 nlink; + uint8 delete_pending; + uint8 directory; + uint32 ea_size; + WIRE_STRING fname; + } out; + } all_info; + + /* RAW_FILEINFO_ALT_NAME_INFO and RAW_FILEINFO_ALT_NAME_INFORMATION interfaces */ + struct { + enum fileinfo_level level; + union smb_fileinfo_in in; + + struct { + WIRE_STRING fname; + } out; + } alt_name_info; + + /* RAW_FILEINFO_STREAM_INFO and RAW_FILEINFO_STREAM_INFORMATION interfaces */ + struct { + enum fileinfo_level level; + union smb_fileinfo_in in; + + struct { + uint_t num_streams; + struct stream_struct *streams; + } out; + } stream_info; + + /* RAW_FILEINFO_COMPRESSION_INFO and RAW_FILEINFO_COMPRESSION_INFORMATION interfaces */ + struct { + enum fileinfo_level level; + union smb_fileinfo_in in; + + struct { + large_t compressed_size; + uint16 format; + uint8 unit_shift; + uint8 chunk_shift; + uint8 cluster_shift; + } out; + } compression_info; + + /* RAW_FILEINFO_UNIX_BASIC interface */ + struct { + enum fileinfo_level level; + union smb_fileinfo_in in; + + struct { + large_t end_of_file; + large_t num_bytes; + NTTIME status_change_time; + NTTIME access_time; + NTTIME change_time; + large_t uid; + large_t gid; + uint32 file_type; + large_t dev_major; + large_t dev_minor; + large_t unique_id; + large_t permissions; + large_t nlink; + } out; + } unix_basic_info; + + /* RAW_FILEINFO_UNIX_LINK interface */ + struct { + enum fileinfo_level level; + union smb_fileinfo_in in; + + struct { + WIRE_STRING link_dest; + } out; + } unix_link_info; + + /* RAW_FILEINFO_INTERNAL_INFORMATION interface */ + struct { + enum fileinfo_level level; + union smb_fileinfo_in in; + + struct { + /* REWRITE: these are very uncertain - we need + * to look at this interface */ + uint32 device; + uint32 inode; + } out; + } internal_information; + + /* RAW_FILEINFO_ACCESS_INFORMATION interface */ + struct { + enum fileinfo_level level; + union smb_fileinfo_in in; + + struct { + uint32 access_flags; /* seen 0x001f01ff from w2k3 */ + } out; + } access_information; + + /* RAW_FILEINFO_POSITION_INFORMATION interface */ + struct { + enum fileinfo_level level; + union smb_fileinfo_in in; + + struct { + large_t position; + } out; + } position_information; + + /* RAW_FILEINFO_MODE_INFORMATION interface */ + struct { + enum fileinfo_level level; + union smb_fileinfo_in in; + + struct { + uint32 mode; + } out; + } mode_information; + + /* RAW_FILEINFO_ALIGNMENT_INFORMATION interface */ + struct { + enum fileinfo_level level; + union smb_fileinfo_in in; + + struct { + uint32 alignment_requirement; + } out; + } alignment_information; + + /* RAW_FILEINFO_NETWORK_OPEN_INFORMATION interface */ + struct { + enum fileinfo_level level; + union smb_fileinfo_in in; + + struct { + NTTIME create_time; + NTTIME access_time; + NTTIME write_time; + NTTIME change_time; + large_t alloc_size; + large_t size; + uint32 attrib; + } out; + } network_open_information; + + + /* RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION interface */ + struct { + enum fileinfo_level level; + union smb_fileinfo_in in; + + struct { + uint32 attrib; + uint32 reparse_tag; + } out; + } attribute_tag_information; +}; + + +enum setfileinfo_level { + RAW_SFILEINFO_GENERIC = 0xF000, + RAW_SFILEINFO_SETATTR, /* SMBsetatr */ + RAW_SFILEINFO_SETATTRE, /* SMBsetattrE */ + RAW_SFILEINFO_STANDARD = SMB_SFILEINFO_STANDARD, + RAW_SFILEINFO_EA_SET = SMB_SFILEINFO_EA_SET, + RAW_SFILEINFO_BASIC_INFO = SMB_SFILEINFO_BASIC_INFO, + RAW_SFILEINFO_DISPOSITION_INFO = SMB_SFILEINFO_DISPOSITION_INFO, + RAW_SFILEINFO_ALLOCATION_INFO = SMB_SFILEINFO_ALLOCATION_INFO, + RAW_SFILEINFO_END_OF_FILE_INFO = SMB_SFILEINFO_END_OF_FILE_INFO, + RAW_SFILEINFO_UNIX_BASIC = SMB_SFILEINFO_UNIX_BASIC, + RAW_SFILEINFO_UNIX_LINK = SMB_SFILEINFO_UNIX_LINK, + RAW_SFILEINFO_UNIX_HLINK = SMB_SFILEINFO_UNIX_HLINK, + RAW_SFILEINFO_BASIC_INFORMATION = SMB_SFILEINFO_BASIC_INFORMATION, + RAW_SFILEINFO_RENAME_INFORMATION = SMB_SFILEINFO_RENAME_INFORMATION, + RAW_SFILEINFO_DISPOSITION_INFORMATION = SMB_SFILEINFO_DISPOSITION_INFORMATION, + RAW_SFILEINFO_POSITION_INFORMATION = SMB_SFILEINFO_POSITION_INFORMATION, + RAW_SFILEINFO_MODE_INFORMATION = SMB_SFILEINFO_MODE_INFORMATION, + RAW_SFILEINFO_ALLOCATION_INFORMATION = SMB_SFILEINFO_ALLOCATION_INFORMATION, + RAW_SFILEINFO_END_OF_FILE_INFORMATION = SMB_SFILEINFO_END_OF_FILE_INFORMATION, + RAW_SFILEINFO_1023 = SMB_SFILEINFO_1023, + RAW_SFILEINFO_1025 = SMB_SFILEINFO_1025, + RAW_SFILEINFO_1029 = SMB_SFILEINFO_1029, + RAW_SFILEINFO_1032 = SMB_SFILEINFO_1032, + RAW_SFILEINFO_1039 = SMB_SFILEINFO_1039, + RAW_SFILEINFO_1040 = SMB_SFILEINFO_1040 +}; + +/* union used in setfileinfo() and setpathinfo() calls */ +union smb_setfileinfo { + /* generic interface */ + struct { + enum setfileinfo_level level; + + /* we are combining setfileinfo and setpathinfo into one + interface */ + union setfileinfo_file { + const char *fname; + uint16 fnum; + } file; + } generic; + + /* RAW_SFILEINFO_SETATTR (SMBsetatr) interface - only via setpathinfo() */ + struct { + enum setfileinfo_level level; + union setfileinfo_file file; + struct { + uint16 attrib; + time_t write_time; + } in; + } setattr; + + /* RAW_SFILEINFO_SETATTRE (SMBsetattrE) interface - only via setfileinfo() */ + struct { + enum setfileinfo_level level; + union setfileinfo_file file; + + struct { + time_t create_time; + time_t access_time; + time_t write_time; + } in; + } setattre; + + + /* RAW_SFILEINFO_STANDARD interface */ + struct { + enum setfileinfo_level level; + union setfileinfo_file file; + struct { + time_t create_time; + time_t access_time; + time_t write_time; + /* notice that size, alloc_size and attrib are not settable, + unlike the corresponding qfileinfo level */ + } in; + } standard; + + /* RAW_SFILEINFO_EA_SET interface */ + struct { + enum setfileinfo_level level; + union setfileinfo_file file; + struct { + struct ea_struct ea; + } in; + } ea_set; + + /* RAW_SFILEINFO_BASIC_INFO and + RAW_SFILEINFO_BASIC_INFORMATION interfaces */ + struct { + enum setfileinfo_level level; + union setfileinfo_file file; + + struct { + NTTIME create_time; + NTTIME access_time; + NTTIME write_time; + NTTIME change_time; + uint32 attrib; + } in; + } basic_info; + + /* RAW_SFILEINFO_DISPOSITION_INFO and + RAW_SFILEINFO_DISPOSITION_INFORMATION interfaces */ + struct { + enum setfileinfo_level level; + union setfileinfo_file file; + + struct { + BOOL delete_on_close; + } in; + } disposition_info; + + /* RAW_SFILEINFO_ALLOCATION_INFO and + RAW_SFILEINFO_ALLOCATION_INFORMATION interfaces */ + struct { + enum setfileinfo_level level; + union setfileinfo_file file; + + struct { + /* w2k3 rounds this up to nearest 4096 */ + large_t alloc_size; + } in; + } allocation_info; + + /* RAW_SFILEINFO_END_OF_FILE_INFO and + RAW_SFILEINFO_END_OF_FILE_INFORMATION interfaces */ + struct { + enum setfileinfo_level level; + union setfileinfo_file file; + + struct { + large_t size; + } in; + } end_of_file_info; + + /* RAW_SFILEINFO_RENAME_INFORMATION interface */ + struct { + enum setfileinfo_level level; + union setfileinfo_file file; + + struct { + uint8 overwrite; + uint32 root_fid; + const char *new_name; + } in; + } rename_information; + + /* RAW_SFILEINFO_POSITION_INFORMATION interface */ + struct { + enum setfileinfo_level level; + union setfileinfo_file file; + + struct { + large_t position; + } in; + } position_information; + + /* RAW_SFILEINFO_MODE_INFORMATION interface */ + struct { + enum setfileinfo_level level; + union setfileinfo_file file; + + struct { + /* valid values seem to be 0, 2, 4 and 6 */ + uint32 mode; + } in; + } mode_information; + + + + /* RAW_SFILEINFO_UNIX_BASIC interface */ + struct { + enum setfileinfo_level level; + union setfileinfo_file file; + struct { + uint32 mode; /* yuck - this field remains to fix compile of libcli/clifile.c */ + large_t end_of_file; + large_t num_bytes; + NTTIME status_change_time; + NTTIME access_time; + NTTIME change_time; + large_t uid; + large_t gid; + uint32 file_type; + large_t dev_major; + large_t dev_minor; + large_t unique_id; + large_t permissions; + large_t nlink; + } in; + } unix_basic; + + /* RAW_SFILEINFO_UNIX_LINK, RAW_SFILEINFO_UNIX_HLINK interface */ + struct { + enum setfileinfo_level level; + union setfileinfo_file file; + struct { + const char *link_dest; + } in; + } unix_link, unix_hlink; +}; + + +enum fsinfo_level {RAW_QFS_GENERIC = 0xF000, + RAW_QFS_DSKATTR, /* SMBdskattr */ + RAW_QFS_ALLOCATION = SMB_QFS_ALLOCATION, + RAW_QFS_VOLUME = SMB_QFS_VOLUME, + RAW_QFS_VOLUME_INFO = SMB_QFS_VOLUME_INFO, + RAW_QFS_SIZE_INFO = SMB_QFS_SIZE_INFO, + RAW_QFS_DEVICE_INFO = SMB_QFS_DEVICE_INFO, + RAW_QFS_ATTRIBUTE_INFO = SMB_QFS_ATTRIBUTE_INFO, + RAW_QFS_UNIX_INFO = SMB_QFS_UNIX_INFO, + RAW_QFS_VOLUME_INFORMATION = SMB_QFS_VOLUME_INFORMATION, + RAW_QFS_SIZE_INFORMATION = SMB_QFS_SIZE_INFORMATION, + RAW_QFS_DEVICE_INFORMATION = SMB_QFS_DEVICE_INFORMATION, + RAW_QFS_ATTRIBUTE_INFORMATION = SMB_QFS_ATTRIBUTE_INFORMATION, + RAW_QFS_QUOTA_INFORMATION = SMB_QFS_QUOTA_INFORMATION, + RAW_QFS_FULL_SIZE_INFORMATION = SMB_QFS_FULL_SIZE_INFORMATION, + RAW_QFS_OBJECTID_INFORMATION = SMB_QFS_OBJECTID_INFORMATION}; + + +/* union for fsinfo() backend call. Note that there are no in + structures, as this call only contains out parameters */ +union smb_fsinfo { + /* generic interface */ + struct { + enum fsinfo_level level; + + struct { + uint32 block_size; + large_t blocks_total; + large_t blocks_free; + uint32 fs_id; + NTTIME create_time; + uint32 serial_number; + uint32 fs_attr; + uint32 max_file_component_length; + uint32 device_type; + uint32 device_characteristics; + large_t quota_soft; + large_t quota_hard; + large_t quota_flags; + GUID guid; + char *volume_name; + char *fs_type; + } out; + } generic; + + /* SMBdskattr interface */ + struct { + enum fsinfo_level level; + + struct { + uint16 units_total; + uint16 blocks_per_unit; + uint16 block_size; + uint16 units_free; + } out; + } dskattr; + + /* trans2 RAW_QFS_ALLOCATION interface */ + struct { + enum fsinfo_level level; + + struct { + uint32 fs_id; + uint32 sectors_per_unit; + uint32 total_alloc_units; + uint32 avail_alloc_units; + uint16 bytes_per_sector; + } out; + } allocation; + + /* TRANS2 RAW_QFS_VOLUME interface */ + struct { + enum fsinfo_level level; + + struct { + uint32 serial_number; + WIRE_STRING volume_name; + } out; + } volume; + + /* TRANS2 RAW_QFS_VOLUME_INFO and RAW_QFS_VOLUME_INFORMATION interfaces */ + struct { + enum fsinfo_level level; + + struct { + NTTIME create_time; + uint32 serial_number; + WIRE_STRING volume_name; + } out; + } volume_info; + + /* trans2 RAW_QFS_SIZE_INFO and RAW_QFS_SIZE_INFORMATION interfaces */ + struct { + enum fsinfo_level level; + + struct { + large_t total_alloc_units; + large_t avail_alloc_units; /* maps to call_avail_alloc_units */ + uint32 sectors_per_unit; + uint32 bytes_per_sector; + } out; + } size_info; + + /* TRANS2 RAW_QFS_DEVICE_INFO and RAW_QFS_DEVICE_INFORMATION interfaces */ + struct { + enum fsinfo_level level; + + struct { + uint32 device_type; + uint32 characteristics; + } out; + } device_info; + + + /* TRANS2 RAW_QFS_ATTRIBUTE_INFO and RAW_QFS_ATTRIBUTE_INFORMATION interfaces */ + struct { + enum fsinfo_level level; + + struct { + uint32 fs_attr; + uint32 max_file_component_length; + WIRE_STRING fs_type; + } out; + } attribute_info; + + + /* TRANS2 RAW_QFS_UNIX_INFO interface */ + struct { + enum fsinfo_level level; + + struct { + uint16 major_version; + uint16 minor_version; + large_t capability; + } out; + } unix_info; + + /* trans2 RAW_QFS_QUOTA_INFORMATION interface */ + struct { + enum fsinfo_level level; + + struct { + large_t unknown[3]; + large_t quota_soft; + large_t quota_hard; + large_t quota_flags; + } out; + } quota_information; + + /* trans2 RAW_QFS_FULL_SIZE_INFORMATION interface */ + struct { + enum fsinfo_level level; + + struct { + large_t total_alloc_units; + large_t call_avail_alloc_units; + large_t actual_avail_alloc_units; + uint32 sectors_per_unit; + uint32 bytes_per_sector; + } out; + } full_size_information; + + /* trans2 RAW_QFS_OBJECTID_INFORMATION interface */ + struct { + enum fsinfo_level level; + + struct { + GUID guid; + large_t unknown[6]; + } out; + } objectid_information; +}; + + + +enum open_level {RAW_OPEN_OPEN, RAW_OPEN_OPENX, + RAW_OPEN_MKNEW, RAW_OPEN_CTEMP, RAW_OPEN_SPLOPEN, + RAW_OPEN_NTCREATEX, RAW_OPEN_T2OPEN}; + +/* the generic interface is defined to be equal to the NTCREATEX interface */ +#define RAW_OPEN_GENERIC RAW_OPEN_NTCREATEX + +/* union for open() backend call */ +union smb_open { + /* SMBNTCreateX interface */ + struct { + enum open_level level; + + struct { + uint32 flags; + uint32 root_fid; + uint32 access_mask; + large_t alloc_size; + uint32 file_attr; + uint32 share_access; + uint32 open_disposition; + uint32 create_options; + uint32 impersonation; + uint8 security_flags; + const char *fname; + } in; + + struct { + uint8 oplock_level; + uint16 fnum; + uint32 create_action; + NTTIME create_time; + NTTIME access_time; + NTTIME write_time; + NTTIME change_time; + uint32 attrib; + large_t alloc_size; + large_t size; + uint16 file_type; + uint16 ipc_state; + uint8 is_directory; + } out; + } ntcreatex, generic; + + /* TRANS2_OPEN interface */ + struct { + enum open_level level; + + struct { + uint16 flags; + uint16 open_mode; + uint16 file_attrs; + time_t write_time; + uint16 open_func; + uint32 size; + uint32 timeout; + const char *fname; + uint_t num_eas; + struct ea_struct *eas; + } in; + + struct { + uint16 fnum; + uint16 attrib; + time_t write_time; + uint32 size; + uint16 access; + uint16 ftype; + uint16 devstate; + uint16 action; + uint32 unknown; + } out; + } t2open; + + /* SMBopen interface */ + struct { + enum open_level level; + + struct { + uint16 flags; + uint16 search_attrs; + const char *fname; + } in; + struct { + uint16 fnum; + uint16 attrib; + time_t write_time; + uint32 size; + uint16 rmode; + } out; + } open; + + /* SMBopenX interface */ + struct { + enum open_level level; + + struct { + uint16 flags; + uint16 open_mode; + uint16 search_attrs; /* not honoured by win2003 */ + uint16 file_attrs; + time_t write_time; /* not honoured by win2003 */ + uint16 open_func; + uint32 size; /* note that this sets the + initial file size, not + just allocation size */ + uint32 timeout; /* not honoured by win2003 */ + const char *fname; + } in; + struct { + uint16 fnum; + uint16 attrib; + time_t write_time; + uint32 size; + uint16 access; + uint16 ftype; + uint16 devstate; + uint16 action; + uint32 unique_fid; + uint32 access_mask; + uint32 unknown; + } out; + } openx; + + /* SMBmknew interface */ + struct { + enum open_level level; + + struct { + uint16 attrib; + time_t write_time; + const char *fname; + } in; + struct { + uint16 fnum; + } out; + } mknew; + + /* SMBctemp interface */ + struct { + enum open_level level; + + struct { + uint16 attrib; + time_t write_time; + const char *directory; + } in; + struct { + uint16 fnum; + /* temp name, relative to directory */ + char *name; + } out; + } ctemp; + + /* SMBsplopen interface */ + struct { + enum open_level level; + + struct { + uint16 setup_length; + uint16 mode; + const char *ident; + } in; + struct { + uint16 fnum; + } out; + } splopen; +}; + + + +enum read_level {RAW_READ_GENERIC, RAW_READ_READBRAW, RAW_READ_LOCKREAD, RAW_READ_READ, RAW_READ_READX}; + +/* union for read() backend call + + note that .infoX.out.data will be allocated before the backend is + called. It will be big enough to hold the maximum size asked for +*/ +union smb_read { + /* generic interface */ + struct { + enum read_level level; + + struct { + uint16 fnum; + SMB_BIG_UINT offset; + uint32 size; + } in; + struct { + char *data; + uint32 nread; + } out; + } generic; + + + /* SMBreadbraw interface */ + struct { + enum read_level level; + + struct { + uint16 fnum; + SMB_BIG_UINT offset; + uint16 maxcnt; + uint16 mincnt; + uint32 timeout; + } in; + struct { + char *data; + uint32 nread; + } out; + } readbraw; + + + /* SMBlockandread interface */ + struct { + enum read_level level; + + struct { + uint16 fnum; + uint16 count; + uint32 offset; + uint16 remaining; + } in; + struct { + char *data; + uint16 nread; + } out; + } lockread; + + /* SMBread interface */ + struct { + enum read_level level; + + struct { + uint16 fnum; + uint16 count; + uint32 offset; + uint16 remaining; + } in; + struct { + char *data; + uint16 nread; + } out; + } read; + + /* SMBreadX interface */ + struct { + enum read_level level; + + struct { + uint16 fnum; + SMB_BIG_UINT offset; + uint16 mincnt; + uint16 maxcnt; + uint16 remaining; + } in; + struct { + char *data; + uint16 remaining; + uint16 compaction_mode; + uint16 nread; + } out; + } readx; +}; + + +enum write_level {RAW_WRITE_GENERIC, RAW_WRITE_WRITEUNLOCK, RAW_WRITE_WRITE, + RAW_WRITE_WRITEX, RAW_WRITE_WRITECLOSE, RAW_WRITE_SPLWRITE}; + +/* union for write() backend call +*/ +union smb_write { + /* generic interface */ + struct { + enum write_level level; + + struct { + uint16 fnum; + SMB_BIG_UINT offset; + uint32 count; + const char *data; + } in; + struct { + uint32 nwritten; + } out; + } generic; + + + /* SMBwriteunlock interface */ + struct { + enum write_level level; + + struct { + uint16 fnum; + uint16 count; + uint32 offset; + uint16 remaining; + const char *data; + } in; + struct { + uint32 nwritten; + } out; + } writeunlock; + + /* SMBwrite interface */ + struct { + enum write_level level; + + struct { + uint16 fnum; + uint16 count; + uint32 offset; + uint16 remaining; + const char *data; + } in; + struct { + uint16 nwritten; + } out; + } write; + + /* SMBwriteX interface */ + struct { + enum write_level level; + + struct { + uint16 fnum; + SMB_BIG_UINT offset; + uint16 wmode; + uint16 remaining; + uint32 count; + const char *data; + } in; + struct { + uint32 nwritten; + uint16 remaining; + } out; + } writex; + + /* SMBwriteclose interface */ + struct { + enum write_level level; + + struct { + uint16 fnum; + uint16 count; + uint32 offset; + time_t mtime; + const char *data; + } in; + struct { + uint16 nwritten; + } out; + } writeclose; + + /* SMBsplwrite interface */ + struct { + enum write_level level; + + struct { + uint16 fnum; + uint16 count; + const char *data; + } in; + } splwrite; +}; + + +enum lock_level {RAW_LOCK_GENERIC, RAW_LOCK_LOCK, RAW_LOCK_UNLOCK, RAW_LOCK_LOCKX}; + +/* union for lock() backend call +*/ +union smb_lock { + /* generic interface */ + struct { + enum lock_level level; + + struct { + + } in; + } generic; + + /* SMBlock interface */ + struct { + enum lock_level level; + + struct { + uint16 fnum; + uint32 count; + uint32 offset; + } in; + } lock; + + /* SMBunlock interface */ + struct { + enum lock_level level; + + struct { + uint16 fnum; + uint32 count; + uint32 offset; + } in; + } unlock; + + /* SMBlockingX interface */ + struct { + enum lock_level level; + + struct { + uint16 fnum; + uint16 mode; + uint32 timeout; + uint16 ulock_cnt; + uint16 lock_cnt; + struct smb_lock_entry { + uint16 pid; + SMB_BIG_UINT offset; + SMB_BIG_UINT count; + } *locks; /* unlocks are first in the arrray */ + } in; + } lockx; +}; + + +enum close_enum {RAW_CLOSE_GENERIC, RAW_CLOSE_CLOSE, RAW_CLOSE_SPLCLOSE}; + +/* + union for close() backend call +*/ +union smb_close { + /* generic interface */ + struct { + enum close_enum level; + + struct { + uint16 fnum; + } in; + } generic; + + /* SMBclose interface */ + struct { + enum close_enum level; + + struct { + uint16 fnum; + time_t write_time; + } in; + } close; + + /* SMBsplclose interface - empty! */ + struct { + enum close_enum level; + + struct { + uint16 fnum; + } in; + } splclose; +}; + + +enum lpq_level {RAW_LPQ_GENERIC, RAW_LPQ_RETQ}; + +/* + union for lpq() backend +*/ +union smb_lpq { + /* generic interface */ + struct { + enum lpq_level level; + + } generic; + + + /* SMBsplretq interface */ + struct { + enum lpq_level level; + + struct { + uint16 maxcount; + uint16 startidx; + } in; + struct { + uint16 count; + uint16 restart_idx; + struct { + time_t time; + uint8 status; + uint16 job; + uint32 size; + char *user; + } *queue; + } out; + } retq; +}; + + +/* struct for SMBioctl */ +struct smb_ioctl { + struct { + uint16 fnum; + uint32 request; + } in; + struct { + DATA_BLOB blob; + } out; +}; + +/* struct for SMBflush */ +struct smb_flush { + struct { + uint16 fnum; + } in; +}; + + +/* struct for SMBcopy */ +struct smb_copy { + struct { + uint16 tid2; + uint16 ofun; + uint16 flags; + const char *path1; + const char *path2; + } in; + struct { + uint16 count; + } out; +}; + + +/* struct for transact2 call */ +struct smb_trans2 { + struct { + uint16 max_param; + uint16 max_data; + uint8 max_setup; + uint16 flags; + uint32 timeout; + uint8 setup_count; + uint16 *setup; + DATA_BLOB params; + DATA_BLOB data; + } in; + + struct { + uint8 setup_count; + uint16 *setup; + DATA_BLOB params; + DATA_BLOB data; + } out; +}; + +/* struct for nttransact2 call */ +struct smb_nttrans { + struct { + uint8 max_setup; + uint32 max_param; + uint32 max_data; + uint32 setup_count; + uint16 function; + uint16 *setup; + DATA_BLOB params; + DATA_BLOB data; + } in; + + struct { + uint8 setup_count; + uint16 *setup; + DATA_BLOB params; + DATA_BLOB data; + } out; +}; + + +/* struct for nttrans change notify call */ +struct smb_notify { + struct { + uint32 buffer_size; + uint32 completion_filter; + uint16 fnum; + BOOL recursive; + } in; + + struct { + uint32 num_changes; + struct { + uint32 action; + WIRE_STRING name; + } *changes; + } out; +}; + +/* struct for NT ioctl call */ +struct smb_ntioctl { + struct { + uint32 function; + uint16 fnum; + BOOL fsctl; + uint8 filter; + } in; +}; + + +enum search_level {RAW_SEARCH_GENERIC = 0xF000, + RAW_SEARCH_SEARCH, /* SMBsearch */ + RAW_SEARCH_STANDARD = SMB_FIND_STANDARD, + RAW_SEARCH_EA_SIZE = SMB_FIND_EA_SIZE, + RAW_SEARCH_DIRECTORY_INFO = SMB_FIND_DIRECTORY_INFO, + RAW_SEARCH_FULL_DIRECTORY_INFO = SMB_FIND_FULL_DIRECTORY_INFO, + RAW_SEARCH_NAME_INFO = SMB_FIND_NAME_INFO, + RAW_SEARCH_BOTH_DIRECTORY_INFO = SMB_FIND_BOTH_DIRECTORY_INFO, + RAW_SEARCH_261 = SMB_FIND_261, + RAW_SEARCH_262 = SMB_FIND_262, + RAW_SEARCH_UNIX_INFO = SMB_FIND_UNIX_INFO}; + + +/* union for file search */ +union smb_search_first { + struct { + enum search_level level; + } generic; + + /* search (old) findfirst interface */ + struct { + enum search_level level; + + struct { + uint16 max_count; + uint16 search_attrib; + const char *pattern; + } in; + struct { + int16 count; + } out; + } search_first; + + /* trans2 findfirst interface */ + struct { + enum search_level level; + + struct { + uint16 search_attrib; + uint16 max_count; + uint16 flags; + uint32 storage_type; + const char *pattern; + } in; + struct { + uint16 handle; + uint16 count; + uint16 end_of_search; + } out; + } t2ffirst; +}; + +/* union for file search continue */ +union smb_search_next { + struct { + enum search_level level; + } generic; + + /* search (old) findnext interface */ + struct { + enum search_level level; + + struct { + uint16 max_count; + uint16 search_attrib; + DATA_BLOB search_id; + } in; + struct { + uint16 count; + } out; + } search_next; + + /* trans2 findnext interface */ + struct { + enum search_level level; + + struct { + uint16 handle; + uint16 max_count; + uint32 resume_key; + uint16 flags; + const char *last_name; + } in; + struct { + uint16 count; + uint16 end_of_search; + } out; + } t2fnext; +}; + +/* union for search reply file data */ +union smb_search_data { + /* search (old) findfirst */ + struct { + uint16 attrib; + time_t write_time; + uint32 size; + DATA_BLOB search_id; /* used to resume search from this point */ + char *name; + } search; + + /* trans2 findfirst RAW_SEARCH_STANDARD level */ + struct { + uint32 resume_key; + time_t create_time; + time_t access_time; + time_t write_time; + uint32 size; + uint32 alloc_size; + uint16 attrib; + WIRE_STRING name; + } standard; + + /* trans2 findfirst RAW_SEARCH_EA_SIZE level */ + struct { + uint32 resume_key; + time_t create_time; + time_t access_time; + time_t write_time; + uint32 size; + uint32 alloc_size; + uint16 attrib; + uint32 ea_size; + WIRE_STRING name; + } ea_size; + + /* RAW_SEARCH_DIRECTORY_INFO interface */ + struct { + uint32 file_index; + NTTIME create_time; + NTTIME access_time; + NTTIME write_time; + NTTIME change_time; + large_t size; + large_t alloc_size; + uint32 attrib; + WIRE_STRING name; + } directory_info; + + /* RAW_SEARCH_FULL_DIRECTORY_INFO interface */ + struct { + uint32 file_index; + NTTIME create_time; + NTTIME access_time; + NTTIME write_time; + NTTIME change_time; + large_t size; + large_t alloc_size; + uint32 attrib; + uint32 ea_size; + WIRE_STRING name; + } full_directory_info; + + /* RAW_SEARCH_NAME_INFO interface */ + struct { + uint32 file_index; + WIRE_STRING name; + } name_info; + + /* RAW_SEARCH_BOTH_DIRECTORY_INFO interface */ + struct { + uint32 file_index; + NTTIME create_time; + NTTIME access_time; + NTTIME write_time; + NTTIME change_time; + large_t size; + large_t alloc_size; + uint32 attrib; + uint32 ea_size; + WIRE_STRING short_name; + WIRE_STRING name; + } both_directory_info; + + /* RAW_SEARCH_261 interface */ + struct { + uint32 file_index; + NTTIME create_time; + NTTIME access_time; + NTTIME write_time; + NTTIME change_time; + large_t size; + large_t alloc_size; + uint32 attrib; + uint32 ea_size; + uint32 unknown[3]; + WIRE_STRING name; + } level_261; + + /* RAW_SEARCH_262 interface */ + struct { + uint32 file_index; + NTTIME create_time; + NTTIME access_time; + NTTIME write_time; + NTTIME change_time; + large_t size; + large_t alloc_size; + uint32 attrib; + uint32 ea_size; + uint32 unknown[2]; + WIRE_STRING short_name; + WIRE_STRING name; + } level_262; + + /* RAW_SEARCH_UNIX_INFO interface */ + struct { + large_t end_of_file; + large_t num_bytes; + NTTIME status_change_time; + NTTIME access_time; + NTTIME change_time; + large_t uid; + large_t gid; + uint32 file_type; + large_t dev_major; + large_t dev_minor; + large_t unique_id; + large_t permissions; + large_t nlink; + } unix_info; +}; + + +enum search_close_level {RAW_FINDCLOSE_GENERIC, RAW_FINDCLOSE_CLOSE}; + +/* union for file search close */ +union smb_search_close { + struct { + enum search_close_level level; + } generic; + + /* SMBfindclose interface */ + struct { + enum search_level level; + + struct { + uint16 handle; + } in; + } findclose; +}; + diff --git a/source4/include/smb_macros.h b/source4/include/smb_macros.h new file mode 100644 index 0000000000..f6a9fb0530 --- /dev/null +++ b/source4/include/smb_macros.h @@ -0,0 +1,290 @@ +/* + 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) do { if (!(b)) { \ + DEBUG(0,("PANIC: assert failed at %s(%d)\n", __FILE__, __LINE__)); \ + smb_panic("assert failed"); }} while (0) + +#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 FNUM_OK(fsp,c) (OPEN_FSP(fsp) && (c)==(fsp)->conn) + +#define CHECK_FSP(fsp,conn) if (!FNUM_OK(fsp,conn)) \ + return(ERROR_DOS(ERRDOS,ERRbadfid)); \ + else if((fsp)->fd == -1) \ + return(ERROR_DOS(ERRDOS,ERRbadaccess)) + +#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) ) + +/* translates a connection number into a service number */ +#define SNUM(conn) ((conn)?(conn)->service:-1) + +/* 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)) + +/* REWRITE TODO: remove these smb_xxx macros */ +#define smb_buf(buf) (((char *)(buf)) + MIN_SMB_SIZE + CVAL(buf,HDR_WCT+4)*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))) + + +#define smb_len(buf) (PVAL(buf,3)|(PVAL(buf,2)<<8)|(PVAL(buf,1)<<16)) +#define _smb_setlen(buf,len) do {(buf)[0] = 0; (buf)[1] = ((len)&0x10000)>>16; \ + (buf)[2] = ((len)&0xFF00)>>8; (buf)[3] = (len)&0xFF;} while (0) + +/******************************************************************* +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) + +/***************************************************************** + 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) (cli_state_has_unix_cifs(c)) + +/**************************************************************************** + Make a filename into unix format. +****************************************************************************/ + +#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,'/','\\') + +/******************************************************************* + vfs stat wrapper that calls internal2unix. +********************************************************************/ + +#define vfs_stat(conn, fname, st) ((conn)->vfs_ops.stat((conn), fname,(st))) + +/******************************************************************* + vfs lstat wrapper that calls internal2unix. +********************************************************************/ + +#define vfs_lstat(conn, fname, st) ((conn)->vfs_ops.lstat((conn), fname,(st))) + +/******************************************************************* + vfs fstat wrapper +********************************************************************/ + +#define vfs_fstat(fsp, fd, st) ((fsp)->conn->vfs_ops.fstat((fsp),(fd),(st))) + +/******************************************************************* + vfs rmdir wrapper that calls internal2unix. +********************************************************************/ + +#define vfs_rmdir(conn,fname) ((conn)->vfs_ops.rmdir((conn),fname)) + +/******************************************************************* + vfs Unlink wrapper that calls internal2unix. +********************************************************************/ + +#define vfs_unlink(conn, fname) ((conn)->vfs_ops.unlink((conn),fname)) + +/******************************************************************* + vfs chmod wrapper that calls internal2unix. +********************************************************************/ + +#define vfs_chmod(conn,fname,mode) ((conn)->vfs_ops.chmod((conn),fname,(mode))) + +/******************************************************************* + vfs chown wrapper that calls internal2unix. +********************************************************************/ + +#define vfs_chown(conn,fname,uid,gid) ((conn)->vfs_ops.chown((conn),fname,(uid),(gid))) + +/******************************************************************* + A wrapper for vfs_chdir(). +********************************************************************/ + +#define vfs_chdir(conn,fname) ((conn)->vfs_ops.chdir((conn),fname)) + +#endif /* _SMB_MACROS_H */ diff --git a/source4/include/stamp-h.in b/source4/include/stamp-h.in new file mode 100644 index 0000000000..c9061b3ad3 --- /dev/null +++ b/source4/include/stamp-h.in @@ -0,0 +1 @@ +Sun Jul 18 20:32:29 UTC 1999 diff --git a/source4/include/talloc.h b/source4/include/talloc.h new file mode 100644 index 0000000000..4badddbb88 --- /dev/null +++ b/source4/include/talloc.h @@ -0,0 +1,51 @@ +#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 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the 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. + **/ +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/source4/include/tdbsam2.h b/source4/include/tdbsam2.h new file mode 100644 index 0000000000..0ca9d34618 --- /dev/null +++ b/source4/include/tdbsam2.h @@ -0,0 +1,94 @@ +/* + * Unix SMB/CIFS implementation. + * tdbsam2 genstruct enabled header file + * 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. + */ + + +/* ALL strings assumes UTF8 as encoding */ + +GENSTRUCT struct tdbsam2_domain_data { + uint32 xcounter; /* counter to be updated at any change */ + + SEC_DESC *sec_desc; /* Security Descriptor */ + DOM_SID *user_sid; /* The User SID */ + char *name; _NULLTERM /* NT User Name */ + char *description; _NULLTERM /* Descritpion (Gecos) */ +}; + +GENSTRUCT struct tdbsam2_user_data { + uint32 xcounter; /* counter to be updated at any change */ + + SEC_DESC *sec_desc; /* Security Descriptor */ + DOM_SID *user_sid; /* The User SID */ + char *name; _NULLTERM /* NT User Name */ + char *description; _NULLTERM /* Descritpion (Gecos) */ + + DOM_SID *group_sid; /* The Primary Group SID */ + + NTTIME *logon_time; + NTTIME *logoff_time; + NTTIME *kickoff_time; + NTTIME *pass_last_set_time; + NTTIME *pass_can_change_time; + NTTIME *pass_must_change_time; + + char *full_name; _NULLTERM /* The Full Name */ + char *home_dir; _NULLTERM /* Home Directory */ + char *dir_drive; _NULLTERM /* Drive Letter the home should be mapped to */ + char *logon_script; _NULLTERM /* Logon script path */ + char *profile_path; _NULLTERM /* Profile is stored here */ + char *workstations; _NULLTERM /* List of Workstation names the user is allowed to LogIn */ + char *unknown_str; _NULLTERM /* Guess ... Unknown */ + char *munged_dial; _NULLTERM /* Callback Number */ + + /* passwords are 16 byte leght, pointer is null if no password */ + uint8 *lm_pw_ptr; _LEN(16) /* Lanman hashed password */ + uint8 *nt_pw_ptr; _LEN(16) /* NT hashed password */ + + uint16 logon_divs; /* 168 - num of hours in a week */ + uint32 hours_len; /* normally 21 */ + uint8 *hours; _LEN(hours_len) /* normally 21 bytes (depends on hours_len) */ + + uint32 unknown_3; /* 0x00ff ffff */ + uint32 unknown_5; /* 0x0002 0000 */ + uint32 unknown_6; /* 0x0000 04ec */ +}; + +GENSTRUCT struct tdbsam2_group_data { + uint32 xcounter; /* counter to be updated at any change */ + + SEC_DESC *sec_desc; /* Security Descriptor */ + DOM_SID *group_sid; /* The Group SID */ + char *name; _NULLTERM /* NT User Name */ + char *description; _NULLTERM /* Descritpion (Gecos) */ + + uint32 count; /* number of sids */ + DOM_SID **members; _LEN(count) /* SID array */ +}; + +GENSTRUCT struct tdbsam2_privilege_data { + uint32 xcounter; /* counter to be updated at any change */ + + LUID_ATTR *privilege; /* Privilege */ + char *name; _NULLTERM /* NT User Name */ + char *description; _NULLTERM /* Descritpion (Gecos) */ + + uint32 count; /* number of sids */ + DOM_SID **members; _LEN(count) /* SID array */ +}; + diff --git a/source4/include/trans2.h b/source4/include/trans2.h new file mode 100644 index 0000000000..6a629f8989 --- /dev/null +++ b/source4/include/trans2.h @@ -0,0 +1,428 @@ +/* + Unix SMB/CIFS implementation. + SMB transaction2 handling + Copyright (C) Jeremy Allison 1994-2002. + Copyright (C) Andrew Tridgell 1995-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 _TRANS2_H_ +#define _TRANS2_H_ + +/* These are the TRANS2 sub commands */ +#define TRANSACT2_OPEN 0 +#define TRANSACT2_FINDFIRST 1 +#define TRANSACT2_FINDNEXT 2 +#define TRANSACT2_QFSINFO 3 +#define TRANSACT2_SETFSINFO 4 +#define TRANSACT2_QPATHINFO 5 +#define TRANSACT2_SETPATHINFO 6 +#define TRANSACT2_QFILEINFO 7 +#define TRANSACT2_SETFILEINFO 8 +#define TRANSACT2_FSCTL 9 +#define TRANSACT2_IOCTL 0xA +#define TRANSACT2_FINDNOTIFYFIRST 0xB +#define TRANSACT2_FINDNOTIFYNEXT 0xC +#define TRANSACT2_MKDIR 0xD +#define TRANSACT2_SESSION_SETUP 0xE +#define TRANSACT2_GET_DFS_REFERRAL 0x10 +#define TRANSACT2_REPORT_DFS_INCONSISTANCY 0x11 + + +/* trans2 Query FS info levels */ +/* +w2k3 TRANS2ALIASES: +Checking for QFSINFO aliases + Found level 1 (0x001) of size 18 (0x12) + Found level 2 (0x002) of size 12 (0x0c) + Found level 258 (0x102) of size 26 (0x1a) + Found level 259 (0x103) of size 24 (0x18) + Found level 260 (0x104) of size 8 (0x08) + Found level 261 (0x105) of size 20 (0x14) + Found level 1001 (0x3e9) of size 26 (0x1a) + Found level 1003 (0x3eb) of size 24 (0x18) + Found level 1004 (0x3ec) of size 8 (0x08) + Found level 1005 (0x3ed) of size 20 (0x14) + Found level 1006 (0x3ee) of size 48 (0x30) + Found level 1007 (0x3ef) of size 32 (0x20) + Found level 1008 (0x3f0) of size 64 (0x40) +Found 13 levels with success status + Level 261 (0x105) and level 1005 (0x3ed) are possible aliases + Level 260 (0x104) and level 1004 (0x3ec) are possible aliases + Level 259 (0x103) and level 1003 (0x3eb) are possible aliases + Level 258 (0x102) and level 1001 (0x3e9) are possible aliases +Found 4 aliased levels +*/ +#define SMB_QFS_ALLOCATION 1 +#define SMB_QFS_VOLUME 2 +#define SMB_QFS_VOLUME_INFO 0x102 +#define SMB_QFS_SIZE_INFO 0x103 +#define SMB_QFS_DEVICE_INFO 0x104 +#define SMB_QFS_ATTRIBUTE_INFO 0x105 +#define SMB_QFS_UNIX_INFO 0x200 +#define SMB_QFS_VOLUME_INFORMATION 1001 +#define SMB_QFS_SIZE_INFORMATION 1003 +#define SMB_QFS_DEVICE_INFORMATION 1004 +#define SMB_QFS_ATTRIBUTE_INFORMATION 1005 +#define SMB_QFS_QUOTA_INFORMATION 1006 +#define SMB_QFS_FULL_SIZE_INFORMATION 1007 +#define SMB_QFS_OBJECTID_INFORMATION 1008 + + +/* trans2 qfileinfo/qpathinfo */ +/* w2k3 TRANS2ALIASES: +Checking for QPATHINFO aliases +setting up complex file \qpathinfo_aliases.txt + Found level 1 (0x001) of size 22 (0x16) + Found level 2 (0x002) of size 26 (0x1a) + Found level 4 (0x004) of size 41 (0x29) + Found level 6 (0x006) of size 0 (0x00) + Found level 257 (0x101) of size 40 (0x28) + Found level 258 (0x102) of size 24 (0x18) + Found level 259 (0x103) of size 4 (0x04) + Found level 260 (0x104) of size 48 (0x30) + Found level 263 (0x107) of size 126 (0x7e) + Found level 264 (0x108) of size 28 (0x1c) + Found level 265 (0x109) of size 38 (0x26) + Found level 267 (0x10b) of size 16 (0x10) + Found level 1004 (0x3ec) of size 40 (0x28) + Found level 1005 (0x3ed) of size 24 (0x18) + Found level 1006 (0x3ee) of size 8 (0x08) + Found level 1007 (0x3ef) of size 4 (0x04) + Found level 1008 (0x3f0) of size 4 (0x04) + Found level 1009 (0x3f1) of size 48 (0x30) + Found level 1014 (0x3f6) of size 8 (0x08) + Found level 1016 (0x3f8) of size 4 (0x04) + Found level 1017 (0x3f9) of size 4 (0x04) + Found level 1018 (0x3fa) of size 126 (0x7e) + Found level 1021 (0x3fd) of size 28 (0x1c) + Found level 1022 (0x3fe) of size 38 (0x26) + Found level 1028 (0x404) of size 16 (0x10) + Found level 1034 (0x40a) of size 56 (0x38) + Found level 1035 (0x40b) of size 8 (0x08) +Found 27 levels with success status + Level 267 (0x10b) and level 1028 (0x404) are possible aliases + Level 265 (0x109) and level 1022 (0x3fe) are possible aliases + Level 264 (0x108) and level 1021 (0x3fd) are possible aliases + Level 263 (0x107) and level 1018 (0x3fa) are possible aliases + Level 260 (0x104) and level 1009 (0x3f1) are possible aliases + Level 259 (0x103) and level 1007 (0x3ef) are possible aliases + Level 258 (0x102) and level 1005 (0x3ed) are possible aliases + Level 257 (0x101) and level 1004 (0x3ec) are possible aliases +Found 8 aliased levels +*/ +#define SMB_QFILEINFO_STANDARD 1 +#define SMB_QFILEINFO_EA_SIZE 2 +#define SMB_QFILEINFO_ALL_EAS 4 +#define SMB_QFILEINFO_IS_NAME_VALID 6 /* only for QPATHINFO */ +#define SMB_QFILEINFO_BASIC_INFO 0x101 +#define SMB_QFILEINFO_STANDARD_INFO 0x102 +#define SMB_QFILEINFO_EA_INFO 0x103 +#define SMB_QFILEINFO_NAME_INFO 0x104 +#define SMB_QFILEINFO_ALL_INFO 0x107 +#define SMB_QFILEINFO_ALT_NAME_INFO 0x108 +#define SMB_QFILEINFO_STREAM_INFO 0x109 +#define SMB_QFILEINFO_COMPRESSION_INFO 0x10b +#define SMB_QFILEINFO_UNIX_BASIC 0x200 +#define SMB_QFILEINFO_UNIX_LINK 0x201 +#define SMB_QFILEINFO_BASIC_INFORMATION 1004 +#define SMB_QFILEINFO_STANDARD_INFORMATION 1005 +#define SMB_QFILEINFO_INTERNAL_INFORMATION 1006 +#define SMB_QFILEINFO_EA_INFORMATION 1007 +#define SMB_QFILEINFO_ACCESS_INFORMATION 1008 +#define SMB_QFILEINFO_NAME_INFORMATION 1009 +#define SMB_QFILEINFO_POSITION_INFORMATION 1014 +#define SMB_QFILEINFO_MODE_INFORMATION 1016 +#define SMB_QFILEINFO_ALIGNMENT_INFORMATION 1017 +#define SMB_QFILEINFO_ALL_INFORMATION 1018 +#define SMB_QFILEINFO_ALT_NAME_INFORMATION 1021 +#define SMB_QFILEINFO_STREAM_INFORMATION 1022 +#define SMB_QFILEINFO_COMPRESSION_INFORMATION 1028 +#define SMB_QFILEINFO_NETWORK_OPEN_INFORMATION 1034 +#define SMB_QFILEINFO_ATTRIBUTE_TAG_INFORMATION 1035 + + + +/* trans2 setfileinfo/setpathinfo levels */ +/* +w2k3 TRANS2ALIASES +Checking for SETFILEINFO aliases +setting up complex file \setfileinfo_aliases.txt + Found level 1 (0x001) of size 2 (0x02) + Found level 2 (0x002) of size 2 (0x02) + Found level 257 (0x101) of size 40 (0x28) + Found level 258 (0x102) of size 2 (0x02) + Found level 259 (0x103) of size 8 (0x08) + Found level 260 (0x104) of size 8 (0x08) + Found level 1004 (0x3ec) of size 40 (0x28) + Found level 1010 (0x3f2) of size 2 (0x02) + Found level 1013 (0x3f5) of size 2 (0x02) + Found level 1014 (0x3f6) of size 8 (0x08) + Found level 1016 (0x3f8) of size 4 (0x04) + Found level 1019 (0x3fb) of size 8 (0x08) + Found level 1020 (0x3fc) of size 8 (0x08) + Found level 1023 (0x3ff) of size 8 (0x08) + Found level 1025 (0x401) of size 16 (0x10) + Found level 1029 (0x405) of size 72 (0x48) + Found level 1032 (0x408) of size 56 (0x38) + Found level 1039 (0x40f) of size 8 (0x08) + Found level 1040 (0x410) of size 8 (0x08) +Found 19 valid levels + +Checking for SETPATHINFO aliases + Found level 1004 (0x3ec) of size 40 (0x28) + Found level 1010 (0x3f2) of size 2 (0x02) + Found level 1013 (0x3f5) of size 2 (0x02) + Found level 1014 (0x3f6) of size 8 (0x08) + Found level 1016 (0x3f8) of size 4 (0x04) + Found level 1019 (0x3fb) of size 8 (0x08) + Found level 1020 (0x3fc) of size 8 (0x08) + Found level 1023 (0x3ff) of size 8 (0x08) + Found level 1025 (0x401) of size 16 (0x10) + Found level 1029 (0x405) of size 72 (0x48) + Found level 1032 (0x408) of size 56 (0x38) + Found level 1039 (0x40f) of size 8 (0x08) + Found level 1040 (0x410) of size 8 (0x08) +Found 13 valid levels +*/ +#define SMB_SFILEINFO_STANDARD 1 +#define SMB_SFILEINFO_EA_SET 2 +#define SMB_SFILEINFO_BASIC_INFO 0x101 +#define SMB_SFILEINFO_DISPOSITION_INFO 0x102 +#define SMB_SFILEINFO_ALLOCATION_INFO 0x103 +#define SMB_SFILEINFO_END_OF_FILE_INFO 0x104 +#define SMB_SFILEINFO_UNIX_BASIC 0x200 +#define SMB_SFILEINFO_UNIX_LINK 0x201 +#define SMB_SFILEINFO_BASIC_INFORMATION 1004 +#define SMB_SFILEINFO_RENAME_INFORMATION 1010 +#define SMB_SFILEINFO_DISPOSITION_INFORMATION 1013 +#define SMB_SFILEINFO_POSITION_INFORMATION 1014 +#define SMB_SFILEINFO_MODE_INFORMATION 1016 +#define SMB_SFILEINFO_ALLOCATION_INFORMATION 1019 +#define SMB_SFILEINFO_END_OF_FILE_INFORMATION 1020 + +/* filemon shows FilePipeInformation */ +#define SMB_SFILEINFO_1023 1023 + +/* filemon shows FilePipeRemoteInformation */ +#define SMB_SFILEINFO_1025 1025 + +/* filemon shows CopyOnWriteInformation */ +#define SMB_SFILEINFO_1029 1029 + +/* filemon shows OleClassIdInformation */ +#define SMB_SFILEINFO_1032 1032 + +/* seems to be the file size - perhaps valid data size? + filemon shows 'InheritContentIndexInfo' +*/ +#define SMB_SFILEINFO_1039 1039 + +/* OLE_INFORMATION? */ +#define SMB_SFILEINFO_1040 1040 + + +/* trans2 findfirst levels */ +/* +w2k3 TRANS2ALIASES: +Checking for FINDFIRST aliases + Found level 1 (0x001) of size 68 (0x44) + Found level 2 (0x002) of size 70 (0x46) + Found level 257 (0x101) of size 108 (0x6c) + Found level 258 (0x102) of size 116 (0x74) + Found level 259 (0x103) of size 60 (0x3c) + Found level 260 (0x104) of size 140 (0x8c) + Found level 261 (0x105) of size 124 (0x7c) + Found level 262 (0x106) of size 148 (0x94) +Found 8 levels with success status +Found 0 aliased levels +*/ +#define SMB_FIND_STANDARD 1 +#define SMB_FIND_EA_SIZE 2 +#define SMB_FIND_DIRECTORY_INFO 0x101 +#define SMB_FIND_FULL_DIRECTORY_INFO 0x102 +#define SMB_FIND_NAME_INFO 0x103 +#define SMB_FIND_BOTH_DIRECTORY_INFO 0x104 +#define SMB_FIND_261 0x105 +#define SMB_FIND_262 0x106 +#define SMB_FIND_UNIX_INFO 0x200 + +/* flags on trans2 findfirst/findnext that control search */ +#define FLAG_TRANS2_FIND_CLOSE 0x1 +#define FLAG_TRANS2_FIND_CLOSE_IF_END 0x2 +#define FLAG_TRANS2_FIND_REQUIRE_RESUME 0x4 +#define FLAG_TRANS2_FIND_CONTINUE 0x8 +#define FLAG_TRANS2_FIND_BACKUP_INTENT 0x10 + +/* + * DeviceType and Characteristics returned in a + * SMB_QFS_DEVICE_INFO call. + */ +#define QFS_DEVICETYPE_CD_ROM 0x2 +#define QFS_DEVICETYPE_CD_ROM_FILE_SYSTEM 0x3 +#define QFS_DEVICETYPE_DISK 0x7 +#define QFS_DEVICETYPE_DISK_FILE_SYSTEM 0x8 +#define QFS_DEVICETYPE_FILE_SYSTEM 0x9 + +/* Characteristics. */ +#define QFS_TYPE_REMOVABLE_MEDIA 0x1 +#define QFS_TYPE_READ_ONLY_DEVICE 0x2 +#define QFS_TYPE_FLOPPY 0x4 +#define QFS_TYPE_WORM 0x8 +#define QFS_TYPE_REMOTE 0x10 +#define QFS_TYPE_MOUNTED 0x20 +#define QFS_TYPE_VIRTUAL 0x40 + + +/* + * Thursby MAC extensions.... + */ + +/* + * MAC CIFS Extensions have the range 0x300 - 0x2FF reserved. + * Supposedly Microsoft have agreed to this. + */ + +#define MIN_MAC_INFO_LEVEL 0x300 +#define MAX_MAC_INFO_LEVEL 0x3FF +#define SMB_QFS_MAC_FS_INFO 0x301 + + + +/* UNIX CIFS Extensions - created by HP */ +/* + * UNIX CIFS Extensions have the range 0x200 - 0x2FF reserved. + * Supposedly Microsoft have agreed to this. + */ + +#define MIN_UNIX_INFO_LEVEL 0x200 +#define MAX_UNIX_INFO_LEVEL 0x2FF + +#define INFO_LEVEL_IS_UNIX(level) (((level) >= MIN_UNIX_INFO_LEVEL) && ((level) <= MAX_UNIX_INFO_LEVEL)) + +#define SMB_QFILEINFO_UNIX_BASIC 0x200 /* UNIX File Info*/ +#define SMB_SFILEINFO_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_QFILEINFO_UNIX_LINK 0x201 +#define SMB_SFILEINFO_UNIX_LINK 0x201 +#define SMB_SFILEINFO_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/source4/include/util_getent.h b/source4/include/util_getent.h new file mode 100644 index 0000000000..b67758ba23 --- /dev/null +++ b/source4/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/source4/include/version.h b/source4/include/version.h new file mode 100644 index 0000000000..72b0b12d1d --- /dev/null +++ b/source4/include/version.h @@ -0,0 +1 @@ +#define SAMBA_VERSION "4.0-test" diff --git a/source4/include/vt_mode.h b/source4/include/vt_mode.h new file mode 100644 index 0000000000..85b481122e --- /dev/null +++ b/source4/include/vt_mode.h @@ -0,0 +1,48 @@ +/* vt_mode.h */ +/* +support vtp-sessions + +written by Christian A. Lademann +*/ + +/* +02.05.95:cal:ported to samba-1.9.13 +*/ + +#ifndef __vt_mode_h__ +# define __vt_mode_h__ + +# define VT_CLOSED 0 +# define VT_OPEN 1 + +# define MS_NONE 0 +# define MS_PTY 1 +# define MS_STREAM 2 +# define MS_VTY 3 + +# define VT_MAXREAD 32 + + +# undef EXTERN + +# ifndef __vt_mode_c__ +# define EXTERN extern +# define DEFAULT(v) +# else +# define EXTERN +# define DEFAULT(v) =(v) +# endif + + EXTERN int VT_Status DEFAULT(VT_CLOSED), + VT_Fd DEFAULT(-1), + VT_ChildPID DEFAULT(-1); + + EXTERN BOOL VT_Mode DEFAULT(False), + VT_ChildDied DEFAULT(False); + + EXTERN char *VT_Line DEFAULT(NULL); + +# undef EXTERN + + +#endif /* __vt_mode_h__ */ diff --git a/source4/include/xfile.h b/source4/include/xfile.h new file mode 100644 index 0000000000..89fa9d1e11 --- /dev/null +++ b/source4/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/source4/install-sh b/source4/install-sh new file mode 100644 index 0000000000..58719246f0 --- /dev/null +++ b/source4/install-sh @@ -0,0 +1,238 @@ +#! /bin/sh +# +# install - install a program, script, or datafile +# This comes from X11R5. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. +# + + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +transformbasename="" +transform_arg="" +instcmd="$mvprog" +chmodcmd="$chmodprog 0755" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src="" +dst="" +dir_arg="" + +while [ x"$1" != x ]; do + case $1 in + -c) instcmd="$cpprog" + shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + -s) stripcmd="$stripprog" + shift + continue;; + + -t=*) transformarg=`echo $1 | sed 's/-t=//'` + shift + continue;; + + -b=*) transformbasename=`echo $1 | sed 's/-b=//'` + shift + continue;; + + *) if [ x"$src" = x ] + then + src=$1 + else + # this colon is to work around a 386BSD /bin/sh bug + : + dst=$1 + fi + shift + continue;; + esac +done + +if [ x"$src" = x ] +then + echo "install: no input file specified" + exit 1 +else + true +fi + +if [ x"$dir_arg" != x ]; then + dst=$src + src="" + + if [ -d $dst ]; then + instcmd=: + else + instcmd=mkdir + fi +else + +# Waiting for this to be detected by the "$instcmd $src $dsttmp" command +# might cause directories to be created, which would be especially bad +# if $src (and thus $dsttmp) contains '*'. + + if [ -f $src -o -d $src ] + then + true + else + echo "install: $src does not exist" + exit 1 + fi + + if [ x"$dst" = x ] + then + echo "install: no destination specified" + exit 1 + else + true + fi + +# If destination is a directory, append the input filename; if your system +# does not like double slashes in filenames, you may need to add some logic + + if [ -d $dst ] + then + dst="$dst"/`basename $src` + else + true + fi +fi + +## this sed command emulates the dirname command +dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + +# Make sure that the destination directory exists. +# this part is taken from Noah Friedman's mkinstalldirs script + +# Skip lots of stat calls in the usual case. +if [ ! -d "$dstdir" ]; then +defaultIFS=' +' +IFS="${IFS-${defaultIFS}}" + +oIFS="${IFS}" +# Some sh's can't handle IFS=/ for some reason. +IFS='%' +set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` +IFS="${oIFS}" + +pathcomp='' + +while [ $# -ne 0 ] ; do + pathcomp="${pathcomp}${1}" + shift + + if [ ! -d "${pathcomp}" ] ; + then + $mkdirprog "${pathcomp}" + else + true + fi + + pathcomp="${pathcomp}/" +done +fi + +if [ x"$dir_arg" != x ] +then + $doit $instcmd $dst && + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi +else + +# If we're going to rename the final executable, determine the name now. + + if [ x"$transformarg" = x ] + then + dstfile=`basename $dst` + else + dstfile=`basename $dst $transformbasename | + sed $transformarg`$transformbasename + fi + +# don't allow the sed command to completely eliminate the filename + + if [ x"$dstfile" = x ] + then + dstfile=`basename $dst` + else + true + fi + +# Make a temp file name in the proper directory. + + dsttmp=$dstdir/#inst.$$# + +# Move or copy the file name to the temp name + + $doit $instcmd $src $dsttmp && + + trap "rm -f ${dsttmp}" 0 && + +# and set any options; do chmod last to preserve setuid bits + +# If any of these fail, we abort the whole thing. If we want to +# ignore errors from any of these, just make sure not to ignore +# errors from the above "$doit $instcmd $src $dsttmp" command. + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && + +# Now rename the file to the real destination. + + $doit $rmcmd -f $dstdir/$dstfile && + $doit $mvcmd $dsttmp $dstdir/$dstfile + +fi && + + +exit 0 diff --git a/source4/intl/.cvsignore b/source4/intl/.cvsignore new file mode 100644 index 0000000000..5f2a5c4cf7 --- /dev/null +++ b/source4/intl/.cvsignore @@ -0,0 +1,2 @@ +*.po +*.po32 diff --git a/source4/intl/lang_tdb.c b/source4/intl/lang_tdb.c new file mode 100644 index 0000000000..6879d70d16 --- /dev/null +++ b/source4/intl/lang_tdb.c @@ -0,0 +1,235 @@ +/* + 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, 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 + 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/source4/lib/.cvsignore b/source4/lib/.cvsignore new file mode 100644 index 0000000000..07da2225c7 --- /dev/null +++ b/source4/lib/.cvsignore @@ -0,0 +1,3 @@ +*.po +*.po32 + diff --git a/source4/lib/account_pol.c b/source4/lib/account_pol.c new file mode 100644 index 0000000000..df1479da3a --- /dev/null +++ b/source4/lib/account_pol.c @@ -0,0 +1,169 @@ +/* + * 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; + TALLOC_CTX *mem_ctx; + + if (tdb && local_pid == getpid()) + return True; + mem_ctx = talloc_init("init_account_policy"); + if (!mem_ctx) { + DEBUG(0,("No memory to open account policy database\n")); + return False; + } + tdb = tdb_open_log(lock_path(mem_ctx, "account_policy.tdb"), 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600); + talloc_destroy(mem_ctx); + if (!tdb) { + DEBUG(0,("Failed to open account policy database\n")); + return False; + } + + local_pid = 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, MAX_PASSWORD_AGE); /* 21 days */ + account_policy_set(AP_MIN_PASSWORD_AGE, 0); /* 0 days */ + account_policy_set(AP_LOCK_ACCOUNT_DURATION, 0); /* lockout for 0 minutes */ + account_policy_set(AP_RESET_COUNT_TIME, 0); /* reset immediatly */ + 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; + + init_account_policy(); + + *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; + + init_account_policy(); + + 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/source4/lib/adt_tree.c b/source4/lib/adt_tree.c new file mode 100644 index 0000000000..0bc224ec23 --- /dev/null +++ b/source4/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 = 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; inum_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 ) + tree->free( 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; inum_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 ///... + */ + + 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; ichildren[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; iroot->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/source4/lib/bitmap.c b/source4/lib/bitmap.c new file mode 100644 index 0000000000..1023dd6541 --- /dev/null +++ b/source4/lib/bitmap.c @@ -0,0 +1,163 @@ +/* + 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; +} + +/**************************************************************************** +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/source4/lib/charcnv.c b/source4/lib/charcnv.c new file mode 100644 index 0000000000..90ddbb4d53 --- /dev/null +++ b/source4/lib/charcnv.c @@ -0,0 +1,925 @@ +/* + Unix SMB/CIFS implementation. + Character set conversion Extensions + Copyright (C) Igor Vergeichik 2001 + Copyright (C) Andrew Tridgell 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" + +/** + * @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 + * not 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]; + + +/** + * 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 (!ret || !*ret) ret = "ASCII"; + return ret; +} + +static void lazy_initialize_conv(void) +{ + static int initialized = False; + + if (!initialized) { + initialized = True; + load_case_tables(); + init_iconv(); + init_valid_table(); + } +} + +/** + Initialize iconv conversion descriptors. +**/ + +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;c1from_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,("Conversion from %s to %s not supported\n", + charset_name((charset_t)c1), charset_name((charset_t)c2))); + conv_handles[c1][c2] = NULL; + } + } + } + + if (did_reload) { + init_valid_table(); + } +} + +/** + * Convert string from one encoding to another, making error checking etc + * + * @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 + * @returns the number of bytes occupied in the destination + **/ +ssize_t convert_string(charset_t from, charset_t to, + void const *src, size_t srclen, + void *dest, size_t destlen) +{ + size_t i_len, o_len; + size_t retval; + const char* inbuf = (const char*)src; + char* outbuf = (char*)dest; + smb_iconv_t descriptor; + + if (srclen == (size_t)-1) + srclen = strlen(src)+1; + + lazy_initialize_conv(); + + descriptor = conv_handles[from][to]; + + if (descriptor == (smb_iconv_t)-1 || descriptor == (smb_iconv_t)0) { + /* conversion not supported, use as is */ + size_t len = MIN(srclen,destlen); + memcpy(dest,src,len); + return len; + } + + i_len=srclen; + o_len=destlen; + retval = smb_iconv(descriptor, &inbuf, &i_len, &outbuf, &o_len); + if(retval==(size_t)-1) { + const char *reason="unknown error"; + switch(errno) { + case EINVAL: + reason="Incomplete multibyte sequence"; + break; + case E2BIG: + reason="No more room"; + DEBUG(0, ("convert_string: Required %d, available %d\n", + srclen, 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"; + break; + } + /* smb_panic(reason); */ + } + return destlen-o_len; +} + +/** + * Convert between character sets, allocating a new buffer 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. + **/ + +ssize_t convert_string_allocate(charset_t from, charset_t to, + void const *src, size_t srclen, void **dest) +{ + size_t i_len, o_len, destlen; + size_t retval; + const char *inbuf = (const char *)src; + char *outbuf, *ob; + smb_iconv_t descriptor; + + *dest = NULL; + + if (src == NULL || srclen == (size_t)-1 || srclen == 0) + return (size_t)-1; + + lazy_initialize_conv(); + + descriptor = conv_handles[from][to]; + + if (descriptor == (smb_iconv_t)-1 || descriptor == (smb_iconv_t)0) { + /* conversion not supported, return -1*/ + DEBUG(3, ("convert_string_allocate: conversion not supported!\n")); + return -1; + } + + destlen = MAX(srclen, 512); + outbuf = NULL; +convert: + destlen = destlen * 2; + ob = (char *)realloc(outbuf, destlen); + if (!ob) { + DEBUG(0, ("convert_string_allocate: realloc failed!\n")); + SAFE_FREE(outbuf); + return (size_t)-1; + } + else + outbuf = ob; + i_len = srclen; + o_len = destlen; + retval = smb_iconv(descriptor, + &inbuf, &i_len, + &outbuf, &o_len); + if(retval == (size_t)-1) { + const char *reason="unknown error"; + switch(errno) { + case EINVAL: + reason="Incomplete multibyte sequence"; + break; + case E2BIG: + goto convert; + case EILSEQ: + reason="Illegal multibyte sequence"; + break; + } + DEBUG(0,("Conversion error: %s(%s)\n",reason,inbuf)); + /* smb_panic(reason); */ + return (size_t)-1; + } + + destlen = destlen - o_len; + *dest = (char *)Realloc(ob,destlen); + if (!*dest) { + DEBUG(0, ("convert_string_allocate: out of memory!\n")); + SAFE_FREE(ob); + return (size_t)-1; + } + + return destlen; +} + + +/** + * 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. + **/ +ssize_t convert_string_talloc(TALLOC_CTX *ctx, charset_t from, charset_t to, + void const *src, size_t srclen, const void **dest) +{ + void *alloced_string; + size_t dest_len; + void *dst; + + *dest = NULL; + dest_len=convert_string_allocate(from, to, src, srclen, &alloced_string); + if (dest_len == (size_t)-1) + return (size_t)-1; + dst = talloc(ctx, dest_len + 2); + /* we want to be absolutely sure that the result is terminated */ + memcpy(dst, alloced_string, dest_len); + SSVAL(dst, dest_len, 0); + SAFE_FREE(alloced_string); + if (dst == NULL) + return -1; + *dest = dst; + 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 = convert_string_allocate(CH_UNIX, CH_UCS2, src, srclen, + (void **) &buffer); + if (size == -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); + free(buffer); + return size; +} + +size_t unix_strlower(const char *src, size_t srclen, char *dest, size_t destlen) +{ + size_t size; + smb_ucs2_t *buffer; + + size = convert_string_allocate(CH_UNIX, CH_UCS2, src, srclen, + (void **) &buffer); + if (size == -1) { + smb_panic("failed to create UCS2 buffer"); + } + if (!strlower_w(buffer) && (dest == src)) { + free(buffer); + return srclen; + } + size = convert_string(CH_UCS2, CH_UNIX, buffer, size, dest, destlen); + free(buffer); + return size; +} + +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 + *
+ *
STR_TERMINATE
means include the null termination
+ *
STR_UPPER
means uppercase in the destination
+ *
+ * + * @param dest_len the maximum length in bytes allowed in the + * destination. If @p dest_len is -1 then no maximum is used. + **/ +ssize_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(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); +} + +ssize_t push_ascii_fstring(void *dest, const char *src) +{ + return push_ascii(dest, src, sizeof(fstring), STR_TERMINATE); +} + +ssize_t push_ascii_pstring(void *dest, const char *src) +{ + return push_ascii(dest, src, sizeof(pstring), STR_TERMINATE); +} + +ssize_t push_pstring(void *dest, const char *src) +{ + return push_ascii(dest, src, sizeof(pstring), STR_TERMINATE); +} + +/** + * 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: + *
+ *
STR_TERMINATE
+ *
STR_TERMINATE means the string in @p src + * is null terminated, and src_len is ignored.
+ *
+ * + * @param src_len is the length of the source area in bytes. + * @returns the number of bytes occupied by the string in @p src. + **/ +ssize_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); + + if (dest_len) + dest[MIN(ret, dest_len-1)] = 0; + + return src_len; +} + +ssize_t pull_ascii_pstring(char *dest, const void *src) +{ + return pull_ascii(dest, src, sizeof(pstring), -1, STR_TERMINATE); +} + +ssize_t pull_ascii_fstring(char *dest, const void *src) +{ + return pull_ascii(dest, src, sizeof(fstring), -1, 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: + * + *
+ *
STR_TERMINATE
means include the null termination. + *
STR_UPPER
means uppercase in the destination. + *
STR_NOALIGN
means don't do alignment. + *
+ * + * @param dest_len is the maximum length allowed in the + * destination. If dest_len is -1 then no maxiumum is used. + **/ +ssize_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 = 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(tmpbuf); + src = tmpbuf; + } + + if (flags & STR_TERMINATE) + src_len++; + + 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; + + len += convert_string(CH_UNIX, CH_UCS2, src, src_len, dest, dest_len); + 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. + **/ +ssize_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, (const void **)dest); +} + + +/** + * 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. + **/ + +ssize_t push_ucs2_allocate(smb_ucs2_t **dest, const char *src) +{ + size_t src_len = strlen(src)+1; + + *dest = NULL; + return convert_string_allocate(CH_UNIX, CH_UCS2, src, src_len, (void **)dest); +} + +/** + 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. +**/ + +ssize_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(tmpbuf); + src = tmpbuf; + } + + if (flags & STR_TERMINATE) + src_len++; + + return convert_string(CH_UNIX, CH_UTF8, src, src_len, dest, dest_len); +} + +ssize_t push_utf8_fstring(void *dest, const char *src) +{ + return push_utf8(dest, src, sizeof(fstring), STR_TERMINATE); +} + +ssize_t push_utf8_pstring(void *dest, const char *src) +{ + return push_utf8(dest, src, sizeof(pstring), 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 + **/ + +ssize_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, (const void **)dest); +} + +/** + * 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 + **/ + +ssize_t push_utf8_allocate(char **dest, const char *src) +{ + size_t src_len = strlen(src)+1; + + *dest = NULL; + return convert_string_allocate(CH_UNIX, CH_UTF8, src, src_len, (void **)dest); +} + +/** + 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 > 0) + src_len--; + } + + if (flags & STR_TERMINATE) { + if (src_len == (size_t)-1) { + src_len = strlen_w(src)*2 + 2; + } else { + 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); + if (dest_len) + dest[MIN(ret, dest_len-1)] = 0; + + return src_len; +} + +ssize_t pull_ucs2_pstring(char *dest, const void *src) +{ + return pull_ucs2(NULL, dest, src, sizeof(pstring), -1, STR_TERMINATE); +} + +ssize_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 + **/ + +ssize_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, (const void **)dest); +} + +/** + * 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 + **/ + +ssize_t pull_ucs2_allocate(void **dest, const smb_ucs2_t *src) +{ + size_t src_len = (strlen_w(src)+1) * sizeof(smb_ucs2_t); + *dest = NULL; + return convert_string_allocate(CH_UCS2, CH_UNIX, src, src_len, dest); +} + +/** + Copy a string from a utf-8 source to a unix char* destination. + Flags can have: + STR_TERMINATE means the string in src is null terminated. + if STR_TERMINATE is set then src_len is ignored. + 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. +**/ + +ssize_t pull_utf8(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_UTF8, CH_UNIX, src, src_len, dest, dest_len); + if (dest_len) + dest[MIN(ret, dest_len-1)] = 0; + + return src_len; +} + +ssize_t pull_utf8_pstring(char *dest, const void *src) +{ + return pull_utf8(dest, src, sizeof(pstring), -1, STR_TERMINATE); +} + +ssize_t pull_utf8_fstring(char *dest, const void *src) +{ + return pull_utf8(dest, src, sizeof(fstring), -1, STR_TERMINATE); +} + +/** + * 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 + **/ + +ssize_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, (const void **)dest); +} + +/** + * 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 + **/ + +ssize_t pull_utf8_allocate(void **dest, const char *src) +{ + size_t src_len = strlen(src)+1; + *dest = NULL; + return convert_string_allocate(CH_UTF8, CH_UNIX, src, src_len, dest); +} + +/** + 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. +**/ + +ssize_t push_string(const void *base_ptr, void *dest, const char *src, size_t dest_len, int flags) +{ + if (!(flags & STR_ASCII) && \ + ((flags & STR_UNICODE || \ + (SVAL(base_ptr, NBT_HDR_SIZE+HDR_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. +**/ + +ssize_t pull_string(const void *base_ptr, char *dest, const void *src, size_t dest_len, size_t src_len, int flags) +{ + if (!(flags & STR_ASCII) && \ + ((flags & STR_UNICODE || \ + (SVAL(base_ptr, NBT_HDR_SIZE+HDR_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); +} + +ssize_t align_string(const void *base_ptr, const char *p, int flags) +{ + if (!(flags & STR_ASCII) && \ + ((flags & STR_UNICODE || \ + (SVAL(base_ptr, NBT_HDR_SIZE+HDR_FLG2) & FLAGS2_UNICODE_STRINGS)))) { + return ucs2_align(base_ptr, p, flags); + } + return 0; +} + +/** + Copy a string from a unicode or ascii source (depending on + the packet flags) to a TALLOC'ed 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. +**/ + +ssize_t pull_string_talloc(TALLOC_CTX *ctx, char **dest, const void *src, size_t src_len, int flags) +{ + if (!(flags & STR_ASCII) && \ + (flags & STR_UNICODE)) { + return pull_ucs2_talloc(ctx, dest, src); + } + *dest = NULL; + if (flags & STR_TERMINATE) { + *dest = talloc_strdup(ctx, src); + return strlen(*dest); + } + *dest = talloc_strndup(ctx, src, src_len); + return src_len; +} + +/** + Convert from ucs2 to unix charset and return the + allocated and converted string or NULL if an error occurred. + You must provide a zero terminated string. + The returning string will be zero terminated. +**/ + +char *acnv_u2ux(const smb_ucs2_t *src) +{ + size_t slen; + size_t dlen; + void *dest; + + slen = (strlen_w(src) + 1) * sizeof(smb_ucs2_t); + dlen = convert_string_allocate(CH_UCS2, CH_UNIX, src, slen, &dest); + if (dlen == (size_t)-1) + return NULL; + else + return dest; +} + +/** + Convert from unix to ucs2 charset and return the + allocated and converted string or NULL if an error occurred. + You must provide a zero terminated string. + The returning string will be zero terminated. +**/ + +smb_ucs2_t *acnv_uxu2(const char *src) +{ + size_t slen; + size_t dlen; + void *dest; + + slen = strlen(src) + 1; + dlen = convert_string_allocate(CH_UNIX, CH_UCS2, src, slen, &dest); + if (dlen == (size_t)-1) + return NULL; + else + return dest; +} + +/** + Convert from ucs2 to dos charset and return the + allocated and converted string or NULL if an error occurred. + You must provide a zero terminated string. + The returning string will be zero terminated. +**/ + +char *acnv_u2dos(const smb_ucs2_t *src) +{ + size_t slen; + size_t dlen; + void *dest; + + slen = (strlen_w(src) + 1) * sizeof(smb_ucs2_t); + dlen = convert_string_allocate(CH_UCS2, CH_DOS, src, slen, &dest); + if (dlen == (size_t)-1) + return NULL; + else + return dest; +} + +/** + Convert from dos to ucs2 charset and return the + allocated and converted string or NULL if an error occurred. + You must provide a zero terminated string. + The returning string will be zero terminated. +**/ + +smb_ucs2_t *acnv_dosu2(const char *src) +{ + size_t slen; + size_t dlen; + void *dest; + + slen = strlen(src) + 1; + dlen = convert_string_allocate(CH_DOS, CH_UCS2, src, slen, &dest); + if (dlen == (size_t)-1) + return NULL; + else + return dest; +} diff --git a/source4/lib/cmdline/popt_common.c b/source4/lib/cmdline/popt_common.c new file mode 100644 index 0000000000..ccdecc91cc --- /dev/null +++ b/source4/lib/cmdline/popt_common.c @@ -0,0 +1,327 @@ +/* + 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; + +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_cmdline("log file", logfile); + return; + } + + switch(opt->val) { + case 'd': + lp_set_cmdline("log level", arg); + break; + + case 'V': + printf( "Version %s\n", SAMBA_VERSION ); + exit(0); + break; + + case 's': + if (arg) { + pstrcpy(dyn_CONFIGFILE, arg); + } + break; + + case 'l': + if (arg) { + pstr_sprintf(logfile, "%s/log.%s", arg, pname); + lp_set_cmdline("log file", logfile); + } + break; + + case 'W': + lp_set_cmdline("workgroup", arg); + break; + + case 'n': + lp_set_cmdline("netbios name", arg); + break; + + case 'i': + lp_set_cmdline("netbios scope", 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 + */ + + +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; + 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; + } +} + + + +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, True, "Don't ask for a password" }, + { "kerberos", 'k', POPT_ARG_NONE, &cmdline_auth_info.use_kerberos, True, "Use kerberos (active directory) authentication" }, + { "authentication-file", 'A', POPT_ARG_STRING, NULL, 'A', "Get the credentials from a file", "FILE" }, + POPT_TABLEEND +}; diff --git a/source4/lib/cmdline/readline.c b/source4/lib/cmdline/readline.c new file mode 100644 index 0000000000..c5da88b3e0 --- /dev/null +++ b/source4/lib/cmdline/readline.c @@ -0,0 +1,159 @@ +/* + 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 +# ifdef HAVE_READLINE_HISTORY_H +# include +# endif +# else +# ifdef HAVE_READLINE_H +# include +# ifdef HAVE_HISTORY_H +# include +# 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(const 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; + + do_debug("%s", prompt); + + 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(const 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/source4/lib/crc32.c b/source4/lib/crc32.c new file mode 100644 index 0000000000..86b0bb6fd9 --- /dev/null +++ b/source4/lib/crc32.c @@ -0,0 +1,71 @@ +/* + Unix SMB/CIFS implementation. + crc32 implementation + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +/* table generated using algorithm from Mark Adler */ +static const uint32 crc_table[] = { + 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 +}; + + +/* + see PNG specification or ISO-3309 for details +*/ +uint32 crc32_buffer(const uint8 *buf, int n) +{ + int i; + uint32 ret; + for (ret=~0, i=0;i> 8); + } + return ~ret; +} diff --git a/source4/lib/crypto/crc32.c b/source4/lib/crypto/crc32.c new file mode 100644 index 0000000000..86b0bb6fd9 --- /dev/null +++ b/source4/lib/crypto/crc32.c @@ -0,0 +1,71 @@ +/* + Unix SMB/CIFS implementation. + crc32 implementation + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +/* table generated using algorithm from Mark Adler */ +static const uint32 crc_table[] = { + 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 +}; + + +/* + see PNG specification or ISO-3309 for details +*/ +uint32 crc32_buffer(const uint8 *buf, int n) +{ + int i; + uint32 ret; + for (ret=~0, i=0;i> 8); + } + return ~ret; +} diff --git a/source4/lib/crypto/hmacmd5.c b/source4/lib/crypto/hmacmd5.c new file mode 100644 index 0000000000..f436fd30c0 --- /dev/null +++ b/source4/lib/crypto/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/source4/lib/crypto/md4.c b/source4/lib/crypto/md4.c new file mode 100644 index 0000000000..417e87bd8e --- /dev/null +++ b/source4/lib/crypto/md4.c @@ -0,0 +1,175 @@ +/* + 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 +*/ + +struct mdfour_state { + 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<>(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(struct mdfour_state *s, uint32 *M) +{ + int j; + uint32 AA, BB, CC, DD; + uint32 X[16]; + + for (j=0;j<16;j++) + X[j] = M[j]; + + AA = s->A; BB = s->B; CC = s->C; DD = s->D; + + ROUND1(s->A,s->B,s->C,s->D, 0, 3); ROUND1(s->D,s->A,s->B,s->C, 1, 7); + ROUND1(s->C,s->D,s->A,s->B, 2, 11); ROUND1(s->B,s->C,s->D,s->A, 3, 19); + ROUND1(s->A,s->B,s->C,s->D, 4, 3); ROUND1(s->D,s->A,s->B,s->C, 5, 7); + ROUND1(s->C,s->D,s->A,s->B, 6, 11); ROUND1(s->B,s->C,s->D,s->A, 7, 19); + ROUND1(s->A,s->B,s->C,s->D, 8, 3); ROUND1(s->D,s->A,s->B,s->C, 9, 7); + ROUND1(s->C,s->D,s->A,s->B, 10, 11); ROUND1(s->B,s->C,s->D,s->A, 11, 19); + ROUND1(s->A,s->B,s->C,s->D, 12, 3); ROUND1(s->D,s->A,s->B,s->C, 13, 7); + ROUND1(s->C,s->D,s->A,s->B, 14, 11); ROUND1(s->B,s->C,s->D,s->A, 15, 19); + + ROUND2(s->A,s->B,s->C,s->D, 0, 3); ROUND2(s->D,s->A,s->B,s->C, 4, 5); + ROUND2(s->C,s->D,s->A,s->B, 8, 9); ROUND2(s->B,s->C,s->D,s->A, 12, 13); + ROUND2(s->A,s->B,s->C,s->D, 1, 3); ROUND2(s->D,s->A,s->B,s->C, 5, 5); + ROUND2(s->C,s->D,s->A,s->B, 9, 9); ROUND2(s->B,s->C,s->D,s->A, 13, 13); + ROUND2(s->A,s->B,s->C,s->D, 2, 3); ROUND2(s->D,s->A,s->B,s->C, 6, 5); + ROUND2(s->C,s->D,s->A,s->B, 10, 9); ROUND2(s->B,s->C,s->D,s->A, 14, 13); + ROUND2(s->A,s->B,s->C,s->D, 3, 3); ROUND2(s->D,s->A,s->B,s->C, 7, 5); + ROUND2(s->C,s->D,s->A,s->B, 11, 9); ROUND2(s->B,s->C,s->D,s->A, 15, 13); + + ROUND3(s->A,s->B,s->C,s->D, 0, 3); ROUND3(s->D,s->A,s->B,s->C, 8, 9); + ROUND3(s->C,s->D,s->A,s->B, 4, 11); ROUND3(s->B,s->C,s->D,s->A, 12, 15); + ROUND3(s->A,s->B,s->C,s->D, 2, 3); ROUND3(s->D,s->A,s->B,s->C, 10, 9); + ROUND3(s->C,s->D,s->A,s->B, 6, 11); ROUND3(s->B,s->C,s->D,s->A, 14, 15); + ROUND3(s->A,s->B,s->C,s->D, 1, 3); ROUND3(s->D,s->A,s->B,s->C, 9, 9); + ROUND3(s->C,s->D,s->A,s->B, 5, 11); ROUND3(s->B,s->C,s->D,s->A, 13, 15); + ROUND3(s->A,s->B,s->C,s->D, 3, 3); ROUND3(s->D,s->A,s->B,s->C, 11, 9); + ROUND3(s->C,s->D,s->A,s->B, 7, 11); ROUND3(s->B,s->C,s->D,s->A, 15, 15); + + s->A += AA; + s->B += BB; + s->C += CC; + s->D += DD; + + s->A &= 0xFFFFFFFF; + s->B &= 0xFFFFFFFF; + s->C &= 0xFFFFFFFF; + s->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; + struct mdfour_state state; + + state.A = 0x67452301; + state.B = 0xefcdab89; + state.C = 0x98badcfe; + state.D = 0x10325476; + + while (n > 64) { + copy64(M, in); + mdfour64(&state, 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(&state, M); + } else { + copy4(buf+120, b); + copy64(M, buf); + mdfour64(&state, M); + copy64(M, buf+64); + mdfour64(&state, M); + } + + for (i=0;i<128;i++) + buf[i] = 0; + copy64(M, buf); + + copy4(out, state.A); + copy4(out+4, state.B); + copy4(out+8, state.C); + copy4(out+12, state.D); +} + + diff --git a/source4/lib/crypto/md5.c b/source4/lib/crypto/md5.c new file mode 100644 index 0000000000..2121b17047 --- /dev/null +++ b/source4/lib/crypto/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<>(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/source4/lib/data_blob.c b/source4/lib/data_blob.c new file mode 100644 index 0000000000..8e7df52bef --- /dev/null +++ b/source4/lib/data_blob.c @@ -0,0 +1,141 @@ +/* + 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 == 0) { + ZERO_STRUCT(ret); + return ret; + } + + if (p == NULL) { + ret.data = talloc(mem_ctx, length); + if (ret.data == NULL) { + smb_panic("data_blob_talloc: talloc_memdup failed.\n"); + } + ret.length = length; + memset(ret.data, 0, ret.length); + return ret; + } + + ret.data = talloc_memdup(mem_ctx, p, length); + if (ret.data == NULL) { + smb_panic("data_blob_talloc: talloc_memdup 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 +*******************************************************************/ +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); +} + + +/******************************************************************* +check if two data blobs are equal +*******************************************************************/ +BOOL data_blob_equal(DATA_BLOB *d1, DATA_BLOB *d2) +{ + if (d1->length != d2->length) { + return False; + } + if (d1->data == d2->data) { + return True; + } + if (d1->data == NULL || d2->data == NULL) { + return False; + } + if (memcmp(d1->data, d2->data, d1->length) == 0) { + return True; + } + return False; +} + diff --git a/source4/lib/debug.c b/source4/lib/debug.c new file mode 100644 index 0000000000..37f93b9ae5 --- /dev/null +++ b/source4/lib/debug.c @@ -0,0 +1,157 @@ +/* + Unix SMB/CIFS implementation. + Samba debug functions + Copyright (C) Andrew Tridgell 2003 + Copyright (C) James J Myers 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 global variable determines what messages are printed */ +int DEBUGLEVEL; + + +/* the registered mutex handlers */ +static struct { + const char *name; + struct debug_ops ops; +} debug_handlers; + +/* state variables for the debug system */ +static struct { + int fd; + enum debug_logtype logtype; + const char *prog_name; +} state; + +/* + the backend for debug messages. Note that the DEBUG() macro has already + ensured that the log level has been met before this is called +*/ +void do_debug(const char *format, ...) +{ + va_list ap; + char *s = NULL; + + if (state.fd == 0) { + reopen_logs(); + } + + if (state.fd <= 0) return; + + va_start(ap, format); + vasprintf(&s, format, ap); + va_end(ap); + + write(state.fd, s, strlen(s)); + free(s); +} + +/* + reopen the log file (usually called because the log file name might have changed) +*/ +void reopen_logs(void) +{ + char *logfile = lp_logfile(); + char *fname = NULL; + int old_fd = state.fd; + + state.fd = 0; + + switch (state.logtype) { + case DEBUG_STDOUT: + state.fd = 1; + break; + + case DEBUG_STDERR: + state.fd = 2; + break; + + case DEBUG_FILE: + if ((*logfile) == '/') { + fname = strdup(logfile); + } else { + asprintf(&fname, "%s/%s.log", dyn_LOGFILEBASE, logfile); + } + if (fname) { + state.fd = open(fname, O_CREAT|O_APPEND|O_WRONLY, 0644); + free(fname); + } + break; + } + + if (old_fd > 2) { + close(old_fd); + } +} + +/* + control the name of the logfile and whether logging will be to stdout, stderr + or a file +*/ +void setup_logging(const char *prog_name, enum debug_logtype new_logtype) +{ + state.logtype = new_logtype; + state.prog_name = prog_name; + reopen_logs(); +} + +/* + return a string constant containing n tabs + no more than 10 tabs are returned +*/ +const char *do_debug_tab(uint_t n) +{ + const char *tabs[] = {"", "\t", "\t\t", "\t\t\t", "\t\t\t\t", "\t\t\t\t\t", + "\t\t\t\t\t\t", "\t\t\t\t\t\t\t", "\t\t\t\t\t\t\t\t", + "\t\t\t\t\t\t\t\t\t", "\t\t\t\t\t\t\t\t\t\t"}; + return tabs[MIN(n, 10)]; +} + + +/* + log/print suspicious usage - print comments and backtrace +*/ +void log_suspicious_usage(const char *from, const char *info) +{ + if (debug_handlers.ops.log_suspicious_usage) { + return debug_handlers.ops.log_suspicious_usage(from, info); + } +} +void print_suspicious_usage(const char* from, const char* info) +{ + if (debug_handlers.ops.print_suspicious_usage) { + return debug_handlers.ops.print_suspicious_usage(from, info); + } +} + +uint32 get_task_id(void) +{ + if (debug_handlers.ops.get_task_id) { + return debug_handlers.ops.get_task_id(); + } + return getpid(); +} + +/* + register a set of debug handlers. +*/ +void register_debug_handlers(const char *name, struct debug_ops *ops) +{ + debug_handlers.name = name; + debug_handlers.ops = *ops; +} diff --git a/source4/lib/dmallocmsg.c b/source4/lib/dmallocmsg.c new file mode 100644 index 0000000000..a83ed518d7 --- /dev/null +++ b/source4/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/source4/lib/dprintf.c b/source4/lib/dprintf.c new file mode 100644 index 0000000000..70387bbd61 --- /dev/null +++ b/source4/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); + + 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/source4/lib/events.c b/source4/lib/events.c new file mode 100644 index 0000000000..f95028b731 --- /dev/null +++ b/source4/lib/events.c @@ -0,0 +1,372 @@ +/* + Unix SMB/CIFS implementation. + main select loop and event handling + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + PLEASE READ THIS BEFORE MODIFYING! + + This module is a general abstraction for the main select loop and + event handling. Do not ever put any localised hacks in here, instead + register one of the possible event types and implement that event + somewhere else. + + There are 4 types of event handling that are handled in this module: + + 1) a file descriptor becoming readable or writeable. This is mostly + used for network sockets, but can be used for any type of file + descriptor. You may only register one handler for each file + descriptor/io combination or you will get unpredictable results + (this means that you can have a handler for read events, and a + separate handler for write events, but not two handlers that are + both handling read events) + + 2) a timed event. You can register an event that happens at a + specific time. You can register as many of these as you + like. When they are called the handler can choose to set the time + for the next event. If next_event is not set then the event is removed. + + 3) an event that happens every time through the select loop. These + sorts of events should be very fast, as they will occur a + lot. Mostly used for things like destroying a talloc context or + checking a signal flag. + + 4) an event triggered by a signal. These can be one shot or + repeated. You can have more than one handler registered for a + single signal if you want to. + + To setup a set of events you first need to create a event_context + structure using the function event_context_init(); This returns a + 'struct event_context' that you use in all subsequent calls. + + After that you can add/remove events that you are interested in + using event_add_*() and event_remove_*(). + + Finally, you call event_loop_wait() to block waiting for one of the + events to occor. In normal operation event_loop_wait() will loop + forever, unless you call event_loop_exit() from inside one of your + handler functions. + +*/ + +#include "includes.h" + +/* + create a event_context structure. This must be the first events + call, and all subsequent calls pass this event_context as the first + element. Event handlers also receive this as their first argument. +*/ +struct event_context *event_context_init(void) +{ + struct event_context *ev; + + ev = malloc(sizeof(*ev)); + if (!ev) return NULL; + + /* start off with no events */ + ZERO_STRUCTP(ev); + + return ev; +} + + + +/* + add a fd based event + return False on failure (memory allocation error) +*/ +BOOL event_add_fd(struct event_context *ev, struct fd_event *e) +{ + e = memdup(e, sizeof(*e)); + if (!e) return False; + DLIST_ADD(ev->fd_events, e); + e->ref_count = 1; + if (e->fd > ev->maxfd) { + ev->maxfd = e->fd; + } + return True; +} + + +/* + recalculate the maxfd +*/ +static void calc_maxfd(struct event_context *ev) +{ + struct fd_event *e; + ev->maxfd = 0; + for (e=ev->fd_events; e; e=e->next) { + if (e->ref_count && + e->fd > ev->maxfd) { + ev->maxfd = e->fd; + } + } +} + +/* + remove a fd based event + the event to remove is matched by looking at the handler + function and the file descriptor + return False on failure (event not found) +*/ +BOOL event_remove_fd(struct event_context *ev, struct fd_event *e1) +{ + struct fd_event *e; + for (e=ev->fd_events; e; e=e->next) { + if (e->ref_count && + e->fd == e1->fd && + e->handler == e1->handler) { + e->ref_count--; + /* possibly calculate the new maxfd */ + calc_maxfd(ev); + return True; + } + } + return False; +} + +/* + remove all fd based events that match a specified fd +*/ +void event_remove_fd_all(struct event_context *ev, int fd) +{ + struct fd_event *e; + for (e=ev->fd_events; e; e=e->next) { + if (e->ref_count && e->fd == fd) { + e->ref_count--; + } + } + calc_maxfd(ev); +} + +/* + remove all fd based events that match a specified handler +*/ +void event_remove_fd_all_handler(struct event_context *ev, void *handler) +{ + struct fd_event *e; + for (e=ev->fd_events; e; e=e->next) { + if (e->ref_count && + handler == (void *)e->handler) { + e->ref_count--; + } + } + calc_maxfd(ev); +} + + +/* + add a timed event + return False on failure (memory allocation error) +*/ +BOOL event_add_timed(struct event_context *ev, struct timed_event *e) +{ + e = memdup(e, sizeof(*e)); + if (!e) return False; + e->ref_count = 1; + DLIST_ADD(ev->timed_events, e); + return True; +} + +/* + remove a timed event + the event to remove is matched only on the handler function + return False on failure (event not found) +*/ +BOOL event_remove_timed(struct event_context *ev, struct timed_event *e1) +{ + struct timed_event *e; + for (e=ev->timed_events; e; e=e->next) { + if (e->ref_count && + e->handler == e1->handler) { + e->ref_count--; + return True; + } + } + return False; +} + +/* + add a loop event + return False on failure (memory allocation error) +*/ +BOOL event_add_loop(struct event_context *ev, struct loop_event *e) +{ + e = memdup(e, sizeof(*e)); + if (!e) return False; + e->ref_count = 1; + DLIST_ADD(ev->loop_events, e); + return True; +} + +/* + remove a loop event + the event to remove is matched only on the handler function + return False on failure (memory allocation error) +*/ +BOOL event_remove_loop(struct event_context *ev, struct loop_event *e1) +{ + struct loop_event *e; + for (e=ev->loop_events; e; e=e->next) { + if (e->ref_count && + e->handler == e1->handler) { + e->ref_count--; + return True; + } + } + return False; +} + + +/* + tell the event loop to exit with the specified code +*/ +void event_loop_exit(struct event_context *ev, int code) +{ + ev->exit.exit_now = True; + ev->exit.code = code; +} + +/* + go into an event loop using the events defined in ev this function + will return with the specified code if one of the handlers calls + event_loop_exit() + + also return (with code 0) if all fd events are removed +*/ +int event_loop_wait(struct event_context *ev) +{ + time_t t; + + ZERO_STRUCT(ev->exit); + + t = time(NULL); + + while (ev->fd_events && !ev->exit.exit_now) { + fd_set r_fds, w_fds; + struct fd_event *fe; + struct loop_event *le; + struct timed_event *te; + int selrtn; + struct timeval tval; + + /* the loop events are called on each loop. Be careful to allow the + event to remove itself */ + for (le=ev->loop_events;le;) { + struct loop_event *next = le->next; + if (le->ref_count == 0) { + DLIST_REMOVE(ev->loop_events, le); + free(le); + } else { + le->ref_count++; + le->handler(ev, le, t); + le->ref_count--; + } + le = next; + } + + ZERO_STRUCT(tval); + FD_ZERO(&r_fds); + FD_ZERO(&w_fds); + + /* setup any fd events */ + for (fe=ev->fd_events; fe; ) { + struct fd_event *next = fe->next; + if (fe->ref_count == 0) { + DLIST_REMOVE(ev->fd_events, fe); + free(fe); + } else { + if (fe->flags & EVENT_FD_READ) { + FD_SET(fe->fd, &r_fds); + } + if (fe->flags & EVENT_FD_WRITE) { + FD_SET(fe->fd, &w_fds); + } + } + fe = next; + } + + /* start with a reasonable max timeout */ + tval.tv_sec = 600; + + /* work out the right timeout for all timed events */ + for (te=ev->timed_events;te;te=te->next) { + int timeout = te->next_event - t; + if (timeout < 0) { + timeout = 0; + } + if (te->ref_count && + timeout < tval.tv_sec) { + tval.tv_sec = timeout; + } + } + + + selrtn = sys_select(ev->maxfd+1, &r_fds, &w_fds, NULL, &tval); + + t = time(NULL); + + if (selrtn == -1 && errno == EBADF) { + /* the socket is dead! this should never + happen as the socket should have first been + made readable and that should have removed + the event, so this must be a bug. This is a + fatal error. */ + DEBUG(0,("EBADF on event_loop_wait - exiting\n")); + return -1; + } + + if (selrtn > 0) { + /* at least one file descriptor is ready - check + which ones and call the handler, being careful to allow + the handler to remove itself when called */ + for (fe=ev->fd_events; fe; fe=fe->next) { + uint16 flags = 0; + if (FD_ISSET(fe->fd, &r_fds)) flags |= EVENT_FD_READ; + if (FD_ISSET(fe->fd, &w_fds)) flags |= EVENT_FD_WRITE; + if (fe->ref_count && flags) { + fe->ref_count++; + fe->handler(ev, fe, t, flags); + fe->ref_count--; + } + } + } + + /* call any timed events that are now due */ + for (te=ev->timed_events;te;) { + struct timed_event *next = te->next; + if (te->ref_count == 0) { + DLIST_REMOVE(ev->timed_events, te); + free(te); + } else if (te->next_event <= t) { + te->ref_count++; + te->handler(ev, te, t); + te->ref_count--; + if (te->next_event <= t) { + /* the handler didn't set a time for the + next event - remove the event */ + event_remove_timed(ev, te); + } + } + te = next; + } + + } + + return ev->exit.code; +} diff --git a/source4/lib/fault.c b/source4/lib/fault.c new file mode 100644 index 0000000000..5a76ce2c0c --- /dev/null +++ b/source4/lib/fault.c @@ -0,0 +1,107 @@ +/* + 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 *); + +/* the registered fault handler */ +static struct { + const char *name; + void (*fault_handler)(int sig); +} fault_handlers; + + +/******************************************************************* +report a fault +********************************************************************/ +static void fault_report(int sig) +{ + static int counter; + + if (counter) _exit(1); + + DEBUG(0,("===============================================================\n")); + DEBUG(0,("INTERNAL ERROR: Signal %d in pid %d (%s)",sig,(int)getpid(),SAMBA_VERSION)); + DEBUG(0,("\nPlease read the file BUGS.txt in the distribution\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 + return; /* this should cause a core dump */ + } + exit(1); +} + +/**************************************************************************** +catch serious errors +****************************************************************************/ +static void sig_fault(int sig) +{ + if (fault_handlers.fault_handler) { + /* we have a fault handler, call it. It may not return. */ + fault_handlers.fault_handler(sig); + } + /* If it returns or doean't exist, use regular reporter */ + 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 +} + +/* + register a fault handler. + Should only be called once in the execution of smbd. +*/ +BOOL register_fault_handler(const char *name, void (*fault_handler)(int sig)) +{ + if (fault_handlers.name != NULL) { + /* it's already registered! */ + DEBUG(2,("fault handler '%s' already registered - failed '%s'\n", + fault_handlers.name, name)); + return False; + } + + fault_handlers.name = name; + fault_handlers.fault_handler = fault_handler; + + DEBUG(2,("fault handler '%s' registered\n", name)); + return True; +} diff --git a/source4/lib/fsusage.c b/source4/lib/fsusage.c new file mode 100644 index 0000000000..bb7cff0645 --- /dev/null +++ b/source4/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/source4/lib/gencache.c b/source4/lib/gencache.c new file mode 100644 index 0000000000..f3740e3e12 --- /dev/null +++ b/source4/lib/gencache.c @@ -0,0 +1,372 @@ +/* + 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 ) +{ + return tdb_lock_bystring(cache, key, 0); +} + +/******************************************************************** + unlock a key +********************************************************************/ + +void gencache_unlock_entry( const char *key ) +{ + tdb_unlock_bystring(cache, key); + return; +} + + diff --git a/source4/lib/genparser.c b/source4/lib/genparser.c new file mode 100644 index 0000000000..39c455def4 --- /dev/null +++ b/source4/lib/genparser.c @@ -0,0 +1,786 @@ +/* + 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. +*/ + +/* + 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>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 = *(const 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; + } + free(s); + return 0; + } + + for (i=0;isize; + + /* 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 *(const int *)ptr; + case sizeof(char): + return *(const 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 = *(const 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(*(const char **)ptr, + pinfo[i].size, len); + } + p2.ptr_count--; + p2.dynamic_len = NULL; + if (gen_dump_array(mem_ctx, &p, &p2, + *(const 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 = strdup(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) { + free(s0); + return -1; + } + } + + free(s0); + 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/source4/lib/genparser_samba.c b/source4/lib/genparser_samba.c new file mode 100644 index 0000000000..bece587747 --- /dev/null +++ b/source4/lib/genparser_samba.c @@ -0,0 +1,200 @@ +/* + 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. +*/ + +#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[GUID_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 < GUID_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 < GUID_SIZE; i++) { + ((GUID *)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; +} + + + +/* 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 < (GUID_SIZE - 1); i++) { + if (!(r = addshort(mem_ctx, p, "%d,", ((GUID *)ptr)->info[i]))) return r; + } + return addshort(mem_ctx, p, "%d", ((GUID *)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); +} + diff --git a/source4/lib/genrand.c b/source4/lib/genrand.c new file mode 100644 index 0000000000..e2e66f7e58 --- /dev/null +++ b/source4/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 = 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/source4/lib/getsmbpass.c b/source4/lib/getsmbpass.c new file mode 100644 index 0000000000..b6ae09b318 --- /dev/null +++ b/source4/lib/getsmbpass.c @@ -0,0 +1,156 @@ +/* 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 */ + +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 SIG_IGN); + + /* 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; + fgets(buf, bufsize, in); + nread = strlen(buf); + if (buf[nread - 1] == '\n') + buf[nread - 1] = '\0'; + + /* Restore echoing. */ + if (echo_off) + (void) tcsetattr (fileno (in), TCSANOW, &t); + + if (in != stdin) + /* We opened the terminal; now close it. */ + fclose (in); + + /* Catch problematic signals */ + CatchSignal(SIGINT, SIGNAL_CAST SIG_DFL); + + printf("\n"); + return buf; +} + +#else + void getsmbpasswd_dummy(void); + void getsmbpasswd_dummy(void) {;} +#endif diff --git a/source4/lib/hmacmd5.c b/source4/lib/hmacmd5.c new file mode 100644 index 0000000000..f436fd30c0 --- /dev/null +++ b/source4/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/source4/lib/iconv.c b/source4/lib/iconv.c new file mode 100644 index 0000000000..8f85e29c2e --- /dev/null +++ b/source4/lib/iconv.c @@ -0,0 +1,526 @@ +/* + Unix SMB/CIFS implementation. + minimal iconv implementation + 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" + + +/** + * @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 *,const char **, size_t *, char **, size_t *); +static size_t ascii_push(void *,const char **, size_t *, char **, size_t *); +static size_t utf8_pull(void *,const char **, size_t *, char **, size_t *); +static size_t utf8_push(void *,const char **, size_t *, char **, size_t *); +static size_t ucs2hex_pull(void *,const char **, size_t *, char **, size_t *); +static size_t ucs2hex_push(void *,const char **, size_t *, char **, size_t *); +static size_t iconv_copy(void *,const 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}, + {"UCS2-HEX", ucs2hex_pull, ucs2hex_push}, + {NULL, NULL, NULL} +}; + +static struct charset_functions *charsets = NULL; + +BOOL smb_register_charset(struct charset_functions *funcs) +{ + struct charset_functions *c = charsets; + + DEBUG(5, ("Attempting to register new charset %s\n", funcs->name)); + /* Check whether we already have this charset... */ + while(c) { + if(!strcasecmp(c->name, funcs->name)){ + DEBUG(2, ("Duplicate charset %s, not registering\n", funcs->name)); + return False; + } + c = c->next; + } + + funcs->next = funcs->prev = NULL; + DEBUG(5, ("Registered charset %s\n", funcs->name)); + DLIST_ADD(charsets, funcs); + return True; +} + +static void lazy_initialize_iconv(void) +{ + static BOOL initialized = False; + int i; + + if (!initialized) { + initialized = True; + for(i = 0; builtin_functions[i].name; i++) + smb_register_charset(&builtin_functions[i]); + } +} + +#ifdef HAVE_NATIVE_ICONV +/* 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, + const char **inbuf, size_t *inbytesleft, + char **outbuf, size_t *outbytesleft) +{ + size_t ret = iconv((iconv_t)cd, + inbuf, inbytesleft, + outbuf, outbytesleft); + if (ret == (size_t)-1) iconv(cd, NULL, NULL, NULL, NULL); + return ret; +} +#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, + const 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, + 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, + 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 (strcmp(fromcode, tocode) == 0) { + ret->direct = iconv_copy; + return ret; + } + + while (from) { + if (strcasecmp(from->name, fromcode) == 0) break; + from = from->next; + } + + while (to) { + if (strcasecmp(to->name, tocode) == 0) break; + to = to->next; + } + +#ifdef HAVE_NATIVE_ICONV + if (!from) { + ret->pull = sys_iconv; + ret->cd_pull = iconv_open("UCS-2LE", fromcode); + if (ret->cd_pull == (iconv_t)-1) goto failed; + } + + if (!to) { + ret->push = sys_iconv; + ret->cd_push = iconv_open(tocode, "UCS-2LE"); + if (ret->cd_push == (iconv_t)-1) goto failed; + } +#else + if (!from || !to) { + goto failed; + } +#endif + + /* check for conversion to/from ucs2 */ + if (strcasecmp(fromcode, "UCS-2LE") == 0 && to) { + ret->direct = to->push; + return ret; + } + if (strcasecmp(tocode, "UCS-2LE") == 0 && from) { + ret->direct = from->pull; + return ret; + } + +#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 + + /* the general case has to go via a buffer */ + if (!ret->pull) ret->pull = from->pull; + if (!ret->push) ret->push = to->push; + return ret; + +failed: + SAFE_FREE(ret); + errno = EINVAL; + return (smb_iconv_t)-1; +} + +/* + 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, const 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, const 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 ucs2hex_pull(void *cd, const 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, const 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, const 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, const char **inbuf, size_t *inbytesleft, + char **outbuf, size_t *outbytesleft) +{ + while (*inbytesleft >= 1 && *outbytesleft >= 2) { + const unsigned char *c = (const 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, const char **inbuf, size_t *inbytesleft, + char **outbuf, size_t *outbytesleft) +{ + while (*inbytesleft >= 2 && *outbytesleft >= 1) { + unsigned char *c = (unsigned char *)*outbuf; + const unsigned char *uc = (const 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/source4/lib/interface.c b/source4/lib/interface.c new file mode 100644 index 0000000000..2540c898ff --- /dev/null +++ b/source4/lib/interface.c @@ -0,0 +1,333 @@ +/* + 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; + +static 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(TALLOC_CTX *mem_ctx, 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 2) { + nmask = *interpret_addr2(mem_ctx, 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 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 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 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/source4/lib/interfaces.c b/source4/lib/interfaces.c new file mode 100644 index 0000000000..96f4b4cd94 --- /dev/null +++ b/source4/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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#endif + +#ifndef SIOCGIFCONF +#ifdef HAVE_SYS_SOCKIO_H +#include +#endif +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif + +#ifdef HAVE_STRING_H +#include +#endif + +#ifdef HAVE_STRINGS_H +#include +#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 +#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; isin_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, + Copyright (C) 2003 Andrew Bartlett + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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/source4/lib/md4.c b/source4/lib/md4.c new file mode 100644 index 0000000000..417e87bd8e --- /dev/null +++ b/source4/lib/md4.c @@ -0,0 +1,175 @@ +/* + 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 +*/ + +struct mdfour_state { + 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<>(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(struct mdfour_state *s, uint32 *M) +{ + int j; + uint32 AA, BB, CC, DD; + uint32 X[16]; + + for (j=0;j<16;j++) + X[j] = M[j]; + + AA = s->A; BB = s->B; CC = s->C; DD = s->D; + + ROUND1(s->A,s->B,s->C,s->D, 0, 3); ROUND1(s->D,s->A,s->B,s->C, 1, 7); + ROUND1(s->C,s->D,s->A,s->B, 2, 11); ROUND1(s->B,s->C,s->D,s->A, 3, 19); + ROUND1(s->A,s->B,s->C,s->D, 4, 3); ROUND1(s->D,s->A,s->B,s->C, 5, 7); + ROUND1(s->C,s->D,s->A,s->B, 6, 11); ROUND1(s->B,s->C,s->D,s->A, 7, 19); + ROUND1(s->A,s->B,s->C,s->D, 8, 3); ROUND1(s->D,s->A,s->B,s->C, 9, 7); + ROUND1(s->C,s->D,s->A,s->B, 10, 11); ROUND1(s->B,s->C,s->D,s->A, 11, 19); + ROUND1(s->A,s->B,s->C,s->D, 12, 3); ROUND1(s->D,s->A,s->B,s->C, 13, 7); + ROUND1(s->C,s->D,s->A,s->B, 14, 11); ROUND1(s->B,s->C,s->D,s->A, 15, 19); + + ROUND2(s->A,s->B,s->C,s->D, 0, 3); ROUND2(s->D,s->A,s->B,s->C, 4, 5); + ROUND2(s->C,s->D,s->A,s->B, 8, 9); ROUND2(s->B,s->C,s->D,s->A, 12, 13); + ROUND2(s->A,s->B,s->C,s->D, 1, 3); ROUND2(s->D,s->A,s->B,s->C, 5, 5); + ROUND2(s->C,s->D,s->A,s->B, 9, 9); ROUND2(s->B,s->C,s->D,s->A, 13, 13); + ROUND2(s->A,s->B,s->C,s->D, 2, 3); ROUND2(s->D,s->A,s->B,s->C, 6, 5); + ROUND2(s->C,s->D,s->A,s->B, 10, 9); ROUND2(s->B,s->C,s->D,s->A, 14, 13); + ROUND2(s->A,s->B,s->C,s->D, 3, 3); ROUND2(s->D,s->A,s->B,s->C, 7, 5); + ROUND2(s->C,s->D,s->A,s->B, 11, 9); ROUND2(s->B,s->C,s->D,s->A, 15, 13); + + ROUND3(s->A,s->B,s->C,s->D, 0, 3); ROUND3(s->D,s->A,s->B,s->C, 8, 9); + ROUND3(s->C,s->D,s->A,s->B, 4, 11); ROUND3(s->B,s->C,s->D,s->A, 12, 15); + ROUND3(s->A,s->B,s->C,s->D, 2, 3); ROUND3(s->D,s->A,s->B,s->C, 10, 9); + ROUND3(s->C,s->D,s->A,s->B, 6, 11); ROUND3(s->B,s->C,s->D,s->A, 14, 15); + ROUND3(s->A,s->B,s->C,s->D, 1, 3); ROUND3(s->D,s->A,s->B,s->C, 9, 9); + ROUND3(s->C,s->D,s->A,s->B, 5, 11); ROUND3(s->B,s->C,s->D,s->A, 13, 15); + ROUND3(s->A,s->B,s->C,s->D, 3, 3); ROUND3(s->D,s->A,s->B,s->C, 11, 9); + ROUND3(s->C,s->D,s->A,s->B, 7, 11); ROUND3(s->B,s->C,s->D,s->A, 15, 15); + + s->A += AA; + s->B += BB; + s->C += CC; + s->D += DD; + + s->A &= 0xFFFFFFFF; + s->B &= 0xFFFFFFFF; + s->C &= 0xFFFFFFFF; + s->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; + struct mdfour_state state; + + state.A = 0x67452301; + state.B = 0xefcdab89; + state.C = 0x98badcfe; + state.D = 0x10325476; + + while (n > 64) { + copy64(M, in); + mdfour64(&state, 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(&state, M); + } else { + copy4(buf+120, b); + copy64(M, buf); + mdfour64(&state, M); + copy64(M, buf+64); + mdfour64(&state, M); + } + + for (i=0;i<128;i++) + buf[i] = 0; + copy64(M, buf); + + copy4(out, state.A); + copy4(out+4, state.B); + copy4(out+8, state.C); + copy4(out+12, state.D); +} + + diff --git a/source4/lib/md5.c b/source4/lib/md5.c new file mode 100644 index 0000000000..2121b17047 --- /dev/null +++ b/source4/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<>(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/source4/lib/messages.c b/source4/lib/messages.c new file mode 100644 index 0000000000..cb26b356bd --- /dev/null +++ b/source4/lib/messages.c @@ -0,0 +1,566 @@ +/* + 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) +{ + TALLOC_CTX *mem_ctx; + + if (tdb) return True; + + mem_ctx = talloc_init("message_init"); + if (!mem_ctx) { + DEBUG(0,("ERROR: No memory to initialise messages database\n")); + return False; + } + tdb = tdb_open_log(lock_path(mem_ctx, "messages.tdb"), + 0, TDB_CLEAR_IF_FIRST|TDB_DEFAULT, + O_RDWR|O_CREAT,0600); + talloc_destroy(mem_ctx); + + 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); + + 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 = 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); +} + +/**************************************************************************** + 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(getpid()); + + tdb_chainlock(tdb, kbuf); + 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)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/source4/lib/module.c b/source4/lib/module.c new file mode 100644 index 0000000000..152e893100 --- /dev/null +++ b/source4/lib/module.c @@ -0,0 +1,128 @@ +/* + Unix SMB/CIFS implementation. + module loading system + + 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" + +#ifdef HAVE_DLOPEN +int smb_load_module(const char *module_name) +{ + void *handle; + init_module_function *init; + int 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) { + DEBUG(0, ("Error loading module '%s': %s\n", module_name, sys_dlerror())); + return False; + } + + init = 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 False; + } + + status = init(); + + DEBUG(2, ("Module '%s' loaded\n", module_name)); + + return status; +} + +/* 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(smb_load_module(modules[i])) { + success++; + } + } + + DEBUG(2, ("%d modules successfully loaded\n", success)); + + return success; +} + +int smb_probe_module(const char *subsystem, const char *module) +{ + char *full_path; + int rc; + TALLOC_CTX *mem_ctx; + + /* Check for absolute path */ + if(module[0] == '/')return smb_load_module(module); + + mem_ctx = talloc_init("smb_probe_module"); + if (!mem_ctx) { + DEBUG(0,("No memory for loading modules\n")); + return False; + } + full_path = talloc_strdup(mem_ctx, lib_path(mem_ctx, subsystem)); + full_path = talloc_asprintf(mem_ctx, "%s/%s.%s", + full_path, module, shlib_ext()); + + rc = smb_load_module(full_path); + talloc_destroy(mem_ctx); + return rc; +} + +#else /* HAVE_DLOPEN */ + +int smb_load_module(const char *module_name) +{ + DEBUG(0,("This samba executable has not been built with plugin support")); + return False; +} + +int smb_load_modules(const char **modules) +{ + DEBUG(0,("This samba executable has not been built with plugin support")); + return False; +} + +int smb_probe_module(const char *subsystem, const char *module) +{ + DEBUG(0,("This samba executable has not been built with plugin support, not probing")); + return False; +} + +#endif /* HAVE_DLOPEN */ + +void init_modules(void) +{ + if(lp_preload_modules()) + smb_load_modules(lp_preload_modules()); + /* FIXME: load static modules */ +} diff --git a/source4/lib/ms_fnmatch.c b/source4/lib/ms_fnmatch.c new file mode 100644 index 0000000000..dd015a0ac8 --- /dev/null +++ b/source4/lib/ms_fnmatch.c @@ -0,0 +1,226 @@ +/* + 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 +#include +#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) +{ + 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) == 0) goto match; + if (ms_fnmatch_lanman_core(p, n) == 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) == 0) goto match; + } + break; + + case UCS2_CHAR('<'): + for (; *n; n++) { + if (ms_fnmatch_lanman_core(p, n) == 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) == 0) goto match; + if (*n != UCS2_CHAR('.')) 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; +} + +static int ms_fnmatch_lanman1(const smb_ucs2_t *pattern, const smb_ucs2_t *string) +{ + 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) && + ms_fnmatch_lanman_core(pattern, dot); + } + + return ms_fnmatch_lanman_core(pattern, string); +} + + +/* 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, + enum protocol_types protocol) +{ + const smb_ucs2_t *p = pattern, *n = string; + smb_ucs2_t c; + + if (protocol <= PROTOCOL_LANMAN2) { + return ms_fnmatch_lanman1(pattern, string); + } + + 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) == 0) return 0; + if (ms_fnmatch_w(p, n, protocol) == 0) return 0; + return -1; + } + if (! *n) return ms_fnmatch_w(p, n, protocol); + n++; + break; + + case UCS2_CHAR('*'): + for (; *n; n++) { + if (ms_fnmatch_w(p, n, protocol) == 0) return 0; + } + break; + + case UCS2_CHAR('<'): + for (; *n; n++) { + if (ms_fnmatch_w(p, n, protocol) == 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) == 0) return 0; + if (*n != UCS2_CHAR('.')) return -1; + n++; + break; + + default: + if (c != *n) return -1; + n++; + } + } + + if (! *n) return 0; + + return -1; +} + + +int ms_fnmatch(const char *pattern, const char *string, enum protocol_types protocol) +{ + wpstring p, s; + int ret; + + pstrcpy_wa(p, pattern); + pstrcpy_wa(s, string); + + ret = ms_fnmatch_w(p, s, protocol); +/* DEBUG(0,("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); +} diff --git a/source4/lib/mutex.c b/source4/lib/mutex.c new file mode 100644 index 0000000000..1be23a52ba --- /dev/null +++ b/source4/lib/mutex.c @@ -0,0 +1,142 @@ +/* + Unix SMB/CIFS implementation. + Samba mutex/lock functions + Copyright (C) Andrew Tridgell 2003 + Copyright (C) James J Myers 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 mutex_t mutex_list[MUTEX_MAX]; + +/* the registered mutex handlers */ +static struct { + const char *name; + struct mutex_ops ops; +} mutex_handlers; + +int mutex_lock_by_id(enum mutex_id id, const char *name) +{ + return mutex_lock(&mutex_list[id], name); +} + +int mutex_unlock_by_id(enum mutex_id id, const char *name) +{ + return mutex_unlock(&mutex_list[id], name); +} + +int mutex_init(mutex_t *mutex, const char *name) +{ + if (mutex_handlers.ops.mutex_init) { + return mutex_handlers.ops.mutex_init(mutex, name); + } + return 0; +} + +int mutex_destroy(mutex_t *mutex, const char *name) +{ + if (mutex_handlers.ops.mutex_destroy) { + return mutex_handlers.ops.mutex_destroy(mutex, name); + } + return 0; +} + +int mutex_lock(mutex_t *mutex, const char *name) +{ + if (mutex_handlers.ops.mutex_lock) { + return mutex_handlers.ops.mutex_lock(mutex, name); + } + return 0; +} + +int mutex_unlock(mutex_t *mutex, const char *name) +{ + if (mutex_handlers.ops.mutex_unlock) { + return mutex_handlers.ops.mutex_unlock(mutex, name); + } + return 0; +} + +/* read/write lock routines */ + +int rwlock_init(rwlock_t *rwlock, const char *name) +{ + if (mutex_handlers.ops.rwlock_init) { + return mutex_handlers.ops.rwlock_init(rwlock, name); + } + return 0; +} + +int rwlock_destroy(rwlock_t *rwlock, const char *name) +{ + if (mutex_handlers.ops.rwlock_destroy) { + return mutex_handlers.ops.rwlock_destroy(rwlock, name); + } + return 0; +} + +int rwlock_lock_write(rwlock_t *rwlock, const char *name) +{ + if (mutex_handlers.ops.rwlock_lock_write) { + return mutex_handlers.ops.rwlock_lock_write(rwlock, name); + } + return 0; +} + +int rwlock_lock_read(rwlock_t *rwlock, const char *name) +{ + if (mutex_handlers.ops.rwlock_lock_read) { + return mutex_handlers.ops.rwlock_lock_read(rwlock, name); + } + return 0; +} + +int rwlock_unlock(rwlock_t *rwlock, const char *name) +{ + if (mutex_handlers.ops.rwlock_unlock) { + return mutex_handlers.ops.rwlock_unlock(rwlock, name); + } + return 0; +} + + +/* + register a set of mutex/rwlock handlers. + Should only be called once in the execution of smbd. +*/ +BOOL register_mutex_handlers(const char *name, struct mutex_ops *ops) +{ + if (mutex_handlers.name != NULL) { + /* it's already registered! */ + DEBUG(2,("mutex handler '%s' already registered - failed '%s'\n", + mutex_handlers.name, name)); + return False; + } + + mutex_handlers.name = name; + mutex_handlers.ops = *ops; + + if (mutex_handlers.ops.mutex_init) { + enum mutex_id id; + for (id=0; id < MUTEX_MAX; id++) { + mutex_handlers.ops.mutex_init(&mutex_list[id], "mutex_list"); + } + } + + DEBUG(2,("mutex handler '%s' registered\n", name)); + return True; +} + diff --git a/source4/lib/pam_errors.c b/source4/lib/pam_errors.c new file mode 100644 index 0000000000..925441fb1d --- /dev/null +++ b/source4/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 + +#if defined(PAM_AUTHTOK_RECOVERY_ERR) && !defined(PAM_AUTHTOK_RECOVER_ERR) +#define PAM_AUTHTOK_RECOVER_ERR PAM_AUTHTOK_RECOVERY_ERR +#endif + +/* PAM -> NT_STATUS map */ +static const struct { + int pam_code; + NTSTATUS ntstatus; +} pam_to_nt_status_map[] = { + {PAM_OPEN_ERR, NT_STATUS_UNSUCCESSFUL}, + {PAM_SYMBOL_ERR, NT_STATUS_UNSUCCESSFUL}, + {PAM_SERVICE_ERR, NT_STATUS_UNSUCCESSFUL}, + {PAM_SYSTEM_ERR, NT_STATUS_UNSUCCESSFUL}, + {PAM_BUF_ERR, NT_STATUS_UNSUCCESSFUL}, + {PAM_PERM_DENIED, NT_STATUS_ACCESS_DENIED}, + {PAM_AUTH_ERR, NT_STATUS_WRONG_PASSWORD}, + {PAM_CRED_INSUFFICIENT, NT_STATUS_INSUFFICIENT_LOGON_INFO}, /* FIXME: Is this correct? */ + {PAM_AUTHINFO_UNAVAIL, NT_STATUS_LOGON_FAILURE}, + {PAM_USER_UNKNOWN, NT_STATUS_NO_SUCH_USER}, + {PAM_MAXTRIES, NT_STATUS_REMOTE_SESSION_LIMIT}, /* FIXME: Is this correct? */ + {PAM_NEW_AUTHTOK_REQD, NT_STATUS_PASSWORD_MUST_CHANGE}, + {PAM_ACCT_EXPIRED, NT_STATUS_ACCOUNT_EXPIRED}, + {PAM_SESSION_ERR, NT_STATUS_INSUFFICIENT_RESOURCES}, + {PAM_CRED_UNAVAIL, NT_STATUS_NO_TOKEN}, /* FIXME: Is this correct? */ + {PAM_CRED_EXPIRED, NT_STATUS_PASSWORD_EXPIRED}, /* FIXME: Is this correct? */ + {PAM_CRED_ERR, NT_STATUS_UNSUCCESSFUL}, + {PAM_AUTHTOK_ERR, NT_STATUS_UNSUCCESSFUL}, +#ifdef PAM_AUTHTOK_RECOVER_ERR + {PAM_AUTHTOK_RECOVER_ERR, NT_STATUS_UNSUCCESSFUL}, +#endif + {PAM_AUTHTOK_EXPIRED, NT_STATUS_PASSWORD_EXPIRED}, + {PAM_SUCCESS, NT_STATUS_OK} +}; + +/* NT_STATUS -> PAM map */ +static const struct { + NTSTATUS ntstatus; + int pam_code; +} nt_status_to_pam_map[] = { + {NT_STATUS_UNSUCCESSFUL, PAM_SYSTEM_ERR}, + {NT_STATUS_NO_SUCH_USER, PAM_USER_UNKNOWN}, + {NT_STATUS_WRONG_PASSWORD, PAM_AUTH_ERR}, + {NT_STATUS_LOGON_FAILURE, PAM_AUTH_ERR}, + {NT_STATUS_ACCOUNT_EXPIRED, PAM_ACCT_EXPIRED}, + {NT_STATUS_PASSWORD_EXPIRED, PAM_AUTHTOK_EXPIRED}, + {NT_STATUS_PASSWORD_MUST_CHANGE, PAM_NEW_AUTHTOK_REQD}, + {NT_STATUS_OK, PAM_SUCCESS} +}; + +/***************************************************************************** +convert a PAM error to a NT status32 code + *****************************************************************************/ +NTSTATUS pam_to_nt_status(int pam_error) +{ + int i; + if (pam_error == 0) return NT_STATUS_OK; + + for (i=0; NT_STATUS_V(pam_to_nt_status_map[i].ntstatus); i++) { + if (pam_error == pam_to_nt_status_map[i].pam_code) + return pam_to_nt_status_map[i].ntstatus; + } + return NT_STATUS_UNSUCCESSFUL; +} + +/***************************************************************************** +convert an NT status32 code to a PAM error + *****************************************************************************/ +int nt_status_to_pam(NTSTATUS nt_status) +{ + int i; + if NT_STATUS_IS_OK(nt_status) return PAM_SUCCESS; + + for (i=0; NT_STATUS_V(nt_status_to_pam_map[i].ntstatus); i++) { + if (NT_STATUS_EQUAL(nt_status,nt_status_to_pam_map[i].ntstatus)) + return nt_status_to_pam_map[i].pam_code; + } + return PAM_SYSTEM_ERR; +} + +#else + +/***************************************************************************** +convert a PAM error to a NT status32 code + *****************************************************************************/ +NTSTATUS pam_to_nt_status(int pam_error) +{ + if (pam_error == 0) return NT_STATUS_OK; + return NT_STATUS_UNSUCCESSFUL; +} + +/***************************************************************************** +convert an NT status32 code to a PAM error + *****************************************************************************/ +int nt_status_to_pam(NTSTATUS nt_status) +{ + if (NT_STATUS_EQUAL(nt_status, NT_STATUS_OK)) return 0; + return 4; /* PAM_SYSTEM_ERR */ +} + +#endif + diff --git a/source4/lib/pidfile.c b/source4/lib/pidfile.c new file mode 100644 index 0000000000..3471f27b8e --- /dev/null +++ b/source4/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) 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/source4/lib/popt/CHANGES b/source4/lib/popt/CHANGES new file mode 100644 index 0000000000..b6ab2aa308 --- /dev/null +++ b/source4/lib/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/source4/lib/popt/COPYING b/source4/lib/popt/COPYING new file mode 100644 index 0000000000..b4c7ca876c --- /dev/null +++ b/source4/lib/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/source4/lib/popt/README b/source4/lib/popt/README new file mode 100644 index 0000000000..7fccc836ff --- /dev/null +++ b/source4/lib/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/source4/lib/popt/dummy.in b/source4/lib/popt/dummy.in new file mode 100644 index 0000000000..e69de29bb2 diff --git a/source4/lib/popt/findme.c b/source4/lib/popt/findme.c new file mode 100644 index 0000000000..f2ad05bb3f --- /dev/null +++ b/source4/lib/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/source4/lib/popt/findme.h b/source4/lib/popt/findme.h new file mode 100644 index 0000000000..5e93963d60 --- /dev/null +++ b/source4/lib/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/source4/lib/popt/popt.c b/source4/lib/popt/popt.c new file mode 100644 index 0000000000..9fa8650312 --- /dev/null +++ b/source4/lib/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 + * XXX from Norbert Warmuth + */ +#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; iarg_strip)) { + numargs--; + } + } + + for(i=1; iarg_strip)) { + continue; + } else { + if(j /* 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/source4/lib/popt/poptconfig.c b/source4/lib/popt/poptconfig.c new file mode 100644 index 0000000000..eb76941363 --- /dev/null +++ b/source4/lib/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/source4/lib/popt/popthelp.c b/source4/lib/popt/popthelp.c new file mode 100644 index 0000000000..6b790a63e7 --- /dev/null +++ b/source4/lib/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/source4/lib/popt/poptint.h b/source4/lib/popt/poptint.h new file mode 100644 index 0000000000..1847ffafe6 --- /dev/null +++ b/source4/lib/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/source4/lib/popt/poptparse.c b/source4/lib/popt/poptparse.c new file mode 100644 index 0000000000..8f00769be9 --- /dev/null +++ b/source4/lib/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/source4/lib/popt/system.h b/source4/lib/popt/system.h new file mode 100644 index 0000000000..059c045817 --- /dev/null +++ b/source4/lib/popt/system.h @@ -0,0 +1,53 @@ +#include "config.h" + +#include +#include +#include +#include + +#if HAVE_MCHECK_H +#include +#endif + +#include +#include +#include + +#if HAVE_UNISTD_H +#include +#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 +#endif + +/* AIX requires this to be the first thing in the file. */ +#ifndef __GNUC__ +# if HAVE_ALLOCA_H +# include +# 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/source4/lib/popt_common.c b/source4/lib/popt_common.c new file mode 100644 index 0000000000..ccdecc91cc --- /dev/null +++ b/source4/lib/popt_common.c @@ -0,0 +1,327 @@ +/* + 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; + +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_cmdline("log file", logfile); + return; + } + + switch(opt->val) { + case 'd': + lp_set_cmdline("log level", arg); + break; + + case 'V': + printf( "Version %s\n", SAMBA_VERSION ); + exit(0); + break; + + case 's': + if (arg) { + pstrcpy(dyn_CONFIGFILE, arg); + } + break; + + case 'l': + if (arg) { + pstr_sprintf(logfile, "%s/log.%s", arg, pname); + lp_set_cmdline("log file", logfile); + } + break; + + case 'W': + lp_set_cmdline("workgroup", arg); + break; + + case 'n': + lp_set_cmdline("netbios name", arg); + break; + + case 'i': + lp_set_cmdline("netbios scope", 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 + */ + + +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; + 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; + } +} + + + +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, True, "Don't ask for a password" }, + { "kerberos", 'k', POPT_ARG_NONE, &cmdline_auth_info.use_kerberos, True, "Use kerberos (active directory) authentication" }, + { "authentication-file", 'A', POPT_ARG_STRING, NULL, 'A', "Get the credentials from a file", "FILE" }, + POPT_TABLEEND +}; diff --git a/source4/lib/readline.c b/source4/lib/readline.c new file mode 100644 index 0000000000..c5da88b3e0 --- /dev/null +++ b/source4/lib/readline.c @@ -0,0 +1,159 @@ +/* + 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 +# ifdef HAVE_READLINE_HISTORY_H +# include +# endif +# else +# ifdef HAVE_READLINE_H +# include +# ifdef HAVE_HISTORY_H +# include +# 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(const 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; + + do_debug("%s", prompt); + + 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(const 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/source4/lib/replace.c b/source4/lib/replace.c new file mode 100644 index 0000000000..cda379c63f --- /dev/null +++ b/source4/lib/replace.c @@ -0,0 +1,467 @@ +/* + 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+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 +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= 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 */ +#if 0 /* REWRITE: not thread safe */ +#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 */ +#endif +#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 + +#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/source4/lib/select.c b/source4/lib/select.c new file mode 100644 index 0000000000..5d7e4a0ad2 --- /dev/null +++ b/source4/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 != 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 = 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/source4/lib/sendfile.c b/source4/lib/sendfile.c new file mode 100644 index 0000000000..bcc8cb08ca --- /dev/null +++ b/source4/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 + +#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 . + */ + +#include + +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 = 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 +#include + +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 +#include +#include + +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/source4/lib/server_mutex.c b/source4/lib/server_mutex.c new file mode 100644 index 0000000000..878e5497d8 --- /dev/null +++ b/source4/lib/server_mutex.c @@ -0,0 +1,58 @@ +/* + 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; +/* FIXME. ref_count should be allocated per name... JRA. */ +size_t ref_count; + +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, &ref_count)) { + 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, &ref_count); + SAFE_FREE(mutex_server_name); + } +} diff --git a/source4/lib/signal.c b/source4/lib/signal.c new file mode 100644 index 0000000000..bff4b91c1a --- /dev/null +++ b/source4/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/source4/lib/smbpasswd.c b/source4/lib/smbpasswd.c new file mode 100644 index 0000000000..92ae1ffea2 --- /dev/null +++ b/source4/lib/smbpasswd.c @@ -0,0 +1,200 @@ +/* + Unix SMB/CIFS implementation. + + smbpasswd file format routines + + Copyright (C) Andrew Tridgell 1992-1998 + Modified by Jeremy Allison 1995. + Modified by Gerald (Jerry) Carter 2000-2001 + Copyright (C) Tim Potter 2001 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the 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 lib/smbpasswd.c + + The smbpasswd file is used to store encrypted passwords in a similar + fashion to the /etc/passwd file. The format is colon separated fields + with one user per line like so: + + ::::: + + The username and uid must correspond to an entry in the /etc/passwd + file. The lanman and nt password hashes are 32 hex digits corresponding + to the 16-byte lanman and nt hashes respectively. + + The password last change time is stored as a string of the format + LCD- where the change time is expressed as an + + 'N' No password + 'D' Disabled + 'H' Homedir required + 'T' Temp account. + 'U' User account (normal) + 'M' MNS logon user account - what is this ? + 'W' Workstation account + 'S' Server account + 'L' Locked account + 'X' No Xpiry on password + 'I' Interdomain trust account + +*/ + +#include "includes.h" + +/*! Convert 32 hex characters into a 16 byte array. */ + +BOOL smbpasswd_gethexpwd(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_m(hexchars, hinybble); + p2 = strchr_m(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); +} + +/*! Convert a 16-byte array into 32 hex characters. */ + +void smbpasswd_sethexpwd(fstring p, unsigned char *pwd, uint16 acb_info) +{ + if (pwd != NULL) { + int i; + for (i = 0; i < 16; i++) + slprintf(&p[i*2], 3, "%02X", pwd[i]); + } else { + if (acb_info & ACB_PWNOTREQ) + safe_strcpy(p, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX", 33); + else + safe_strcpy(p, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", 33); + } +} + +/*! Decode the account control bits (ACB) info from a string. */ + +uint16 smbpasswd_decode_acb_info(const char *p) +{ + uint16 acb_info = 0; + BOOL finished = False; + + /* + * Check if the account type bits have been encoded after the + * NT password (in the form [NDHTUWSLXI]). + */ + + if (*p != '[') return 0; + + for (p++; *p && !finished; p++) + { + switch (*p) { + case 'N': /* 'N'o password. */ + acb_info |= ACB_PWNOTREQ; + break; + case 'D': /* 'D'isabled. */ + acb_info |= ACB_DISABLED; + break; + case 'H': /* 'H'omedir required. */ + acb_info |= ACB_HOMDIRREQ; + break; + case 'T': /* 'T'emp account. */ + acb_info |= ACB_TEMPDUP; + break; + case 'U': /* 'U'ser account (normal). */ + acb_info |= ACB_NORMAL; + break; + case 'M': /* 'M'NS logon user account. What is this ? */ + acb_info |= ACB_MNS; + break; + case 'W': /* 'W'orkstation account. */ + acb_info |= ACB_WSTRUST; + break; + case 'S': /* 'S'erver account. */ + acb_info |= ACB_SVRTRUST; + break; + case 'L': /* 'L'ocked account. */ + acb_info |= ACB_AUTOLOCK; + break; + case 'X': /* No 'X'piry on password */ + acb_info |= ACB_PWNOEXP; + break; + case 'I': /* 'I'nterdomain trust account. */ + acb_info |= ACB_DOMTRUST; + break; + + case ' ': + break; + case ':': + case '\n': + case '\0': + case ']': + default: + finished = True; + break; + } + } + + return acb_info; +} + +/*! Encode account control bits (ACBs) into a string. */ + +char *smbpasswd_encode_acb_info(uint16 acb_info) +{ + static fstring acct_str; + size_t i = 0; + + acct_str[i++] = '['; + + if (acb_info & ACB_PWNOTREQ ) acct_str[i++] = 'N'; + if (acb_info & ACB_DISABLED ) acct_str[i++] = 'D'; + if (acb_info & ACB_HOMDIRREQ) acct_str[i++] = 'H'; + if (acb_info & ACB_TEMPDUP ) acct_str[i++] = 'T'; + if (acb_info & ACB_NORMAL ) acct_str[i++] = 'U'; + if (acb_info & ACB_MNS ) acct_str[i++] = 'M'; + if (acb_info & ACB_WSTRUST ) acct_str[i++] = 'W'; + if (acb_info & ACB_SVRTRUST ) acct_str[i++] = 'S'; + if (acb_info & ACB_AUTOLOCK ) acct_str[i++] = 'L'; + if (acb_info & ACB_PWNOEXP ) acct_str[i++] = 'X'; + if (acb_info & ACB_DOMTRUST ) acct_str[i++] = 'I'; + + for ( ; i < NEW_PW_FORMAT_SPACE_PADDED_LEN - 2 ; i++ ) + acct_str[i] = ' '; + + i = NEW_PW_FORMAT_SPACE_PADDED_LEN - 2; + acct_str[i++] = ']'; + acct_str[i++] = '\0'; + + return acct_str; +} diff --git a/source4/lib/smbrun.c b/source4/lib/smbrun.c new file mode 100644 index 0000000000..acb836ba7a --- /dev/null +++ b/source4/lib/smbrun.c @@ -0,0 +1,171 @@ +/* + 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=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 */ + } + + execl("/bin/sh","sh","-c",cmd,NULL); + + /* not reached */ + exit(82); + return 1; +} diff --git a/source4/lib/snprintf.c b/source4/lib/snprintf.c new file mode 100644 index 0000000000..1eae2f09b9 --- /dev/null +++ b/source4/lib/snprintf.c @@ -0,0 +1,978 @@ +/* + * 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 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 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 01/27/98 for mutt 0.89i + * The PGP code was using unsigned hexadecimal formats. + * Unfortunately, unsigned formats simply didn't work. + * + * Michael Elkins 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 + * + **************************************************************/ + +#ifndef NO_CONFIG_H /* for some tests */ +#include "config.h" +#else +#define NULL 0 +#endif + +#ifdef TEST_SNPRINTF /* need math library headers for testing */ +#include +#endif + +#ifdef HAVE_STRING_H +#include +#endif + +#ifdef HAVE_STRINGS_H +#include +#endif +#ifdef HAVE_CTYPE_H +#include +#endif +#include +#include +#ifdef HAVE_STDLIB_H +#include +#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 + /* make the compiler happy with an empty file */ + void dummy_snprintf(void) {} +#else + +#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 + +/* 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 + +#ifndef VA_COPY +#ifdef HAVE_VA_COPY +#define VA_COPY(dest, src) __va_copy(dest, src) +#else +#define VA_COPY(dest, src) (dest) = (src) +#endif +#endif + +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); + +/* + * 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 + +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 = ""; + } + + 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 index; + 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); + index = (int) ((temp -intpart +0.05)* 10.0); + /* index = (int) (((double)(temp*0.1) -intpart +0.05) *10.0); */ + /* printf ("%llf, %f, %x\n", temp, intpart, index); */ + iconvert[iplace++] = + (caps? "0123456789ABCDEF":"0123456789abcdef")[index]; + } 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); + index = (int) ((temp -fracpart +0.05)* 10.0); + /* index = (int) ((((temp/10) -fracpart) +0.05) *10); */ + /* printf ("%lf, %lf, %ld\n", temp, fracpart, index); */ + fconvert[fplace++] = + (caps? "0123456789ABCDEF":"0123456789abcdef")[index]; + } 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)++; +} + +/* yes this really must be a ||. Don't muck with this (tridge) */ +#if !defined(HAVE_VSNPRINTF) || !defined(HAVE_C99_VSNPRINTF) + 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 wiith 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. Perhaps + * these should really be smb_snprintf to avoid conflicts with buggy + * linkers? -- mbp + */ +#if !defined(HAVE_SNPRINTF) || !defined(HAVE_C99_SNPRINTF) + int 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, 0203.9, 0.96, 0.996, + 0.9996, 1.996, 4.136, 5.030201, 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 /* SNPRINTF_TEST */ diff --git a/source4/lib/substitute.c b/source4/lib/substitute.c new file mode 100644 index 0000000000..4e4f0bc040 --- /dev/null +++ b/source4/lib/substitute.c @@ -0,0 +1,188 @@ +/* + 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" + +/* oh bugger - I realy didn't want to have a top-level context + anywhere, but until we change all lp_*() calls to take a context + argument this is needed */ +static struct substitute_context *sub; + +void sub_set_context(struct substitute_context *subptr) +{ + sub = subptr; +} + +/* + setup a string in the negotiate structure, using alpha_strcpy with SAFE_NETBIOS_CHARS +*/ +static void setup_string(char **dest, const char *str) +{ + char *s; + + s = strdup(str); + if (!s) { + return; + } + + alpha_strcpy(s, str, SAFE_NETBIOS_CHARS, strlen(s)+1); + + trim_string(s," "," "); + strlower(s); + + SAFE_FREE(*dest); + (*dest) = s; +} + +void sub_set_local_machine(const char *local_machine) +{ + if (!sub) return; + setup_string(&sub->local_machine, local_machine); +} + +void sub_set_remote_machine(const char *remote_machine) +{ + if (!sub) return; + setup_string(&sub->remote_machine, remote_machine); +} + +void sub_set_remote_proto(const char *str) +{ + if (!sub) return; + setup_string(&sub->remote_proto, str); +} + +void sub_set_remote_arch(const char *str) +{ + if (!sub) return; + setup_string(&sub->remote_arch, str); +} + +const char *sub_get_remote_machine(void) +{ + if (!sub) return "UNKNOWN"; + return sub->remote_machine; +} + +const char *sub_get_local_machine(void) +{ + if (!sub) return "UNKNOWN"; + return sub->local_machine; +} + + +/* + setup the string used by %U substitution +*/ +void sub_set_user_name(const char *name) +{ + if (!sub) return; + setup_string(&sub->user_name, name); +} + +/**************************************************************************** +FOO +****************************************************************************/ +void standard_sub_basic(char *str,size_t 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) +{ + return talloc_strdup(mem_ctx, str); +} + +char *alloc_sub_basic(const char *smb_name, const char *str) +{ + return strdup(str); +} + +/**************************************************************************** + 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) +{ + return talloc_strdup(mem_ctx, input_string); +} + +char *alloc_sub_specified(const char *input_string, + const char *username, + const char *domain, + uid_t uid, + gid_t gid) +{ + return strdup(input_string); +} + +char *talloc_sub_advanced(TALLOC_CTX *mem_ctx, + int snum, + const char *user, + const char *connectpath, + gid_t gid, + const char *smb_name, + char *str) +{ + return talloc_strdup(mem_ctx, str); +} + +char *alloc_sub_advanced(int snum, const char *user, + const char *connectpath, gid_t gid, + const char *smb_name, char *str) +{ + return strdup(str); +} + +/**************************************************************************** + Do some standard substitutions in a string. +****************************************************************************/ + +void standard_sub_conn(struct tcon_context *conn, char *str, size_t len) +{ +} + +char *talloc_sub_conn(TALLOC_CTX *mem_ctx, struct tcon_context *conn, char *str) +{ + return talloc_strdup(mem_ctx, str); +} + +char *alloc_sub_conn(struct tcon_context *conn, char *str) +{ + return strdup(str); +} + +/**************************************************************************** + Like standard_sub but by snum. +****************************************************************************/ + +void standard_sub_snum(int snum, char *str, size_t len) +{ +} diff --git a/source4/lib/sysacls.c b/source4/lib/sysacls.c new file mode 100644 index 0000000000..fe85b9e72f --- /dev/null +++ b/source4/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 for UnixWare / OpenUNIX. + * Modified by Toomas Soome 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'; + + /* : : 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 + +/* + * 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; + // REWRITE: add this back in?? + //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'; + + /* : : 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;in_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 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;icount <= 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/source4/lib/system.c b/source4/lib/system.c new file mode 100644 index 0000000000..bafe689a30 --- /dev/null +++ b/source4/lib/system.c @@ -0,0 +1,1114 @@ +/* + 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 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 +} + +/******************************************************************* +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 link +********************************************************************/ + +int sys_link(const char *oldpath, const char *newpath) +{ +#ifndef HAVE_LINK + errno = ENOSYS; + return -1; +#else + return link(oldpath, newpath); +#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 */ +} + +#ifdef HAVE_SETGROUPS + +/************************************************************************** + 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_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 */ +} + +#endif /* HAVE_SETGROUPS */ + +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 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 = 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 +} + diff --git a/source4/lib/system_smbd.c b/source4/lib/system_smbd.c new file mode 100644 index 0000000000..3ae0a6395e --- /dev/null +++ b/source4/lib/system_smbd.c @@ -0,0 +1,119 @@ +/* + 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 (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) +{ +#ifdef HAVE_GETGROUPLIST + return getgrouplist(user, gid, groups, grpcnt); +#else + int retval; + become_root(); + retval = getgrouplist_internals(user, gid, groups, grpcnt); + unbecome_root(); + return retval; +#endif +} diff --git a/source4/lib/talloc.c b/source4/lib/talloc.c new file mode 100644 index 0000000000..83a99d8071 --- /dev/null +++ b/source4/lib/talloc.c @@ -0,0 +1,515 @@ +/* + Samba Unix SMB/CIFS implementation. + Samba temporary memory allocation functions + 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. +*/ + +/** + @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" + +struct talloc_chunk { + struct talloc_chunk *next; + size_t size; + void *ptr; +}; + + +struct talloc_ctx { + struct talloc_chunk *list; + off_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, *prev; +}; + + +/** + * 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; + +/** + * Add to the global list + **/ +static void talloc_enroll(TALLOC_CTX *t) +{ +#if 0 + /* disabled enrole/disenrole until we have __thread support */ + MUTEX_LOCK_BY_ID(MUTEX_TALLOC); + DLIST_ADD(list_head, t); + MUTEX_UNLOCK_BY_ID(MUTEX_TALLOC); +#endif +} + + +static void talloc_disenroll(TALLOC_CTX *t) +{ +#if 0 + /* disabled enrole/disenrole until we have __thread support */ + MUTEX_LOCK_BY_ID(MUTEX_TALLOC); + DLIST_REMOVE(list_head, t); + MUTEX_UNLOCK_BY_ID(MUTEX_TALLOC); +#endif +} + + +/** 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); + SAFE_FREE(t); +} + +/** Return the current total size of the pool. */ +size_t talloc_pool_size(TALLOC_CTX *t) +{ + return t->total_alloc_size; +} + +const char *talloc_pool_name(TALLOC_CTX const *t) +{ + if (t) return t->name; + + 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) +{ + return talloc_memdup(t, p, strlen(p) + 1); +} + +/** strndup with a talloc */ +char *talloc_strndup(TALLOC_CTX *t, const char *p, size_t n) +{ + size_t len = strnlen(p, n); + char *ret; + + ret = talloc(t, len + 1); + if (!ret) { return NULL; } + memcpy(ret, p, len); + ret[len] = 0; + return ret; +} + +/** 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)); + 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) getpid()); + s = talloc_asprintf_append(rt, s, "%-40s %8s %8s\n", + "name", "chunks", "bytes"); + s = talloc_asprintf_append(rt, s, "%-40s %8s %8s\n", + "----------------------------------------", + "--------", + "--------"); + MUTEX_LOCK_BY_ID(MUTEX_TALLOC); + + for (it = list_head; it; it = it->next) { + 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; + } + + MUTEX_UNLOCK_BY_ID(MUTEX_TALLOC); + + 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; + } + } +} + + +/* + move a lump of memory from one talloc context to another + return the ptr on success, or NULL if it could not be found + in the old context or could not be transferred +*/ +const void *talloc_steal(TALLOC_CTX *old_ctx, TALLOC_CTX *new_ctx, const void *ptr) +{ + struct talloc_chunk *tc, *tc2; + + if (!ptr || !old_ctx->list) return NULL; + + /* as a special case, see if its the first element in the + list */ + if (old_ctx->list->ptr == ptr) { + tc = old_ctx->list; + old_ctx->list = old_ctx->list->next; + tc->next = new_ctx->list; + new_ctx->list = tc; + old_ctx->total_alloc_size -= tc->size; + new_ctx->total_alloc_size += tc->size; + return ptr; + } + + /* find it in the old context */ + for (tc=old_ctx->list; tc->next; tc=tc->next) { + if (tc->next->ptr == ptr) break; + } + + if (!tc->next) return NULL; + + /* move it to the new context */ + tc2 = tc->next; + tc->next = tc->next->next; + tc2->next = new_ctx->list; + new_ctx->list = tc2; + old_ctx->total_alloc_size -= tc2->size; + new_ctx->total_alloc_size += tc2->size; + + return ptr; +} + + +/** @} */ diff --git a/source4/lib/tallocmsg.c b/source4/lib/tallocmsg.c new file mode 100644 index 0000000000..bbe1ee60a4 --- /dev/null +++ b/source4/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/source4/lib/talloctort.c b/source4/lib/talloctort.c new file mode 100644 index 0000000000..ad5de38581 --- /dev/null +++ b/source4/lib/talloctort.c @@ -0,0 +1,65 @@ +/* + Unix SMB/CIFS implementation. + Samba temporary memory allocation functions -- torturer + Copyright (C) 2001 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" + +#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 %dkB\n", ctx[i], + talloc_pool_name(ctx[i]), + 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/source4/lib/tdb/README b/source4/lib/tdb/README new file mode 100644 index 0000000000..fac3eacb4d --- /dev/null +++ b/source4/lib/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/source4/lib/tdb/spinlock.c b/source4/lib/tdb/spinlock.c new file mode 100644 index 0000000000..2370ce3bdd --- /dev/null +++ b/source4/lib/tdb/spinlock.c @@ -0,0 +1,430 @@ +/* + Unix SMB/CIFS implementation. + Samba database functions + Copyright (C) Anton Blanchard 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. +*/ +#if HAVE_CONFIG_H +#include +#endif + +#if STANDALONE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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) + +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) +{ + return 0; +} + +/* + * 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 = (hash_size + 1) * sizeof(tdb_rwlock_t); + 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/source4/lib/tdb/spinlock.h b/source4/lib/tdb/spinlock.h new file mode 100644 index 0000000000..d6a2ac6eb8 --- /dev/null +++ b/source4/lib/tdb/spinlock.h @@ -0,0 +1,55 @@ +#ifndef __SPINLOCK_H__ +#define __SPINLOCK_H__ + +#if HAVE_CONFIG_H +#include +#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); + +#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); +#endif + +#endif diff --git a/source4/lib/tdb/tdb.c b/source4/lib/tdb/tdb.c new file mode 100644 index 0000000000..097209ff7a --- /dev/null +++ b/source4/lib/tdb/tdb.c @@ -0,0 +1,2020 @@ + /* + Unix SMB/CIFS implementation. + Samba database functions + Copyright (C) Andrew Tridgell 1999-2000 + Copyright (C) Luke Kenneth Casson Leighton 2000 + Copyright (C) Paul `Rusty' Russell 2000 + Copyright (C) Jeremy Allison 2000-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. +*/ +#ifdef STANDALONE +#if HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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)) + +/* 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) + return TDB_ERRCODE(TDB_ERR_LOCK_TIMEOUT, -1); + /* Otherwise - generic lock error. */ + /* errno set by fcntl */ + 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;iheader.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_HASH_TOP(tdb->header.hash_size-1)) { + 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; + + 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(TDB_CONTEXT *tdb, TDB_DATA key, int locktype, + struct list_struct *rec) +{ + u32 hash, rec_ptr; + + hash = tdb_hash(&key); + 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(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf) +{ + struct list_struct rec; + tdb_off rec_ptr; + + /* find entry */ + if (!(rec_ptr = tdb_find(tdb, key, tdb_hash(&key), &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; + + /* find which hash bucket it is in */ + if (!(rec_ptr = tdb_find_lock(tdb,key,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 +*/ +int tdb_exists(TDB_CONTEXT *tdb, TDB_DATA key) +{ + struct list_struct rec; + + if (tdb_find_lock(tdb, key, F_RDLCK, &rec) == 0) + return 0; + tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK); + return 1; +} + +/* 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 (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 *state) +{ + 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, state)) { + /* 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(tdb, 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 */ +int tdb_delete(TDB_CONTEXT *tdb, TDB_DATA key) +{ + tdb_off rec_ptr; + struct list_struct rec; + int ret; + + if (!(rec_ptr = tdb_find_lock(tdb, key, 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; +} + +/* 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(tdb, key)) { + tdb->ecode = TDB_ERR_EXISTS; + goto fail; + } + } else { + /* first try in-place update, on modify or replace. */ + if (tdb_update(tdb, key, dbuf) == 0) + goto out; + if (flag == TDB_MODIFY && tdb->ecode == TDB_ERR_NOEXIST) + 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(tdb, key); + + /* 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); + + /* now we're into insert / modify / replace of a record which + * we know could not be optimised by an in-place store (for + * various reasons). */ + 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, TDB_DATA new_dbuf) +{ + struct list_struct rec; + tdb_off rec_ptr; + + /* find entry */ + if (!(rec_ptr = tdb_find(tdb, key, tdb_hash(&key), &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, 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(tdb, key); + + 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; + 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 ((locked = (tdb_brlock(tdb, ACTIVE_LOCK, F_WRLCK, F_SETLK, 0) == 0)) + && (tdb_flags & TDB_CLEAR_IF_FIRST)) { + 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 + || (tdb->header.hash_size != hash_size + && !(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; + } + } + /* 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; + 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 is 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_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_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) { + if (tdb_reopen(tdb) != 0) return -1; + } + + return 0; +} diff --git a/source4/lib/tdb/tdb.h b/source4/lib/tdb/tdb.h new file mode 100644 index 0000000000..6f3b1ff756 --- /dev/null +++ b/source4/lib/tdb/tdb.h @@ -0,0 +1,144 @@ +#ifndef __TDB_H__ +#define __TDB_H__ + +/* + Unix SMB/CIFS implementation. + Samba database functions + 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. +*/ + +#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_NOEXIST, TDB_ERR_NOLOCK, TDB_ERR_LOCK_TIMEOUT }; + +#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 *state); +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/source4/lib/tdb/tdb.magic b/source4/lib/tdb/tdb.magic new file mode 100644 index 0000000000..f5619e7327 --- /dev/null +++ b/source4/lib/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/source4/lib/tdb/tdbutil.c b/source4/lib/tdb/tdbutil.c new file mode 100644 index 0000000000..0d8f6128cc --- /dev/null +++ b/source4/lib/tdb/tdbutil.c @@ -0,0 +1,687 @@ +/* + Unix SMB/CIFS implementation. + tdb utility 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" +#include + +/* 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_by_string(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_by_string(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_by_string(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 becouse 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 becouse 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; + 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 'w': + len = 2; + w = (uint16)va_arg(ap, int); + if (bufsize >= len) + SSVAL(buf, 0, w); + break; + case 'd': + len = 4; + d = va_arg(ap, uint32); + if (bufsize >= len) + SIVAL(buf, 0, d); + break; + case 'p': + len = 4; + p = va_arg(ap, void *); + d = p?1:0; + if (bufsize >= len) + SIVAL(buf, 0, d); + break; + case 'P': + s = va_arg(ap,char *); + w = strlen(s); + len = w + 1; + if (bufsize >= len) + memcpy(buf, s, len); + break; + case 'f': + s = va_arg(ap,char *); + w = strlen(s); + len = w + 1; + if (bufsize >= len) + memcpy(buf, s, len); + break; + case 'B': + i = va_arg(ap, int); + s = va_arg(ap, char *); + len = 4+i; + if (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; + bufsize -= len; + } + + 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; + 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 '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; +} + +/**************************************************************************** + 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 = next_node; + }; +}; + + diff --git a/source4/lib/tdb/tdbutil.h b/source4/lib/tdb/tdbutil.h new file mode 100644 index 0000000000..01473446a1 --- /dev/null +++ b/source4/lib/tdb/tdbutil.h @@ -0,0 +1,37 @@ +/* + Unix SMB/CIFS implementation. + tdb utility functions + 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. +*/ + +#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/source4/lib/time.c b/source4/lib/time.c new file mode 100644 index 0000000000..2844da004d --- /dev/null +++ b/source4/lib/time.c @@ -0,0 +1,754 @@ +/* + 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 + 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 +********************************************************************/ +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= dst_table[i].start && t <= dst_table[i].end) break; + + if (i 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 * lp_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 * lp_time_offset(); + int d = TimeZoneFaster(lt); + time_t t = lt + d; + + /* if overflow occurred, ignore all the adjustments so far */ + if (((lte < lt) ^ (lp_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(const 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); + + 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 (!(l_time_min <= d && d <= l_time_max)) + return(0); + + 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); +} + +/**************************************************************************** +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 an NTTIME structure, containing high / low time. convert to unix time. +lkclXXXX this may need 2 SIVALs not a memcpy. we'll see... +****************************************************************************/ +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; + + if (unixdate == 0) { + return 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; + 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 (time_t)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(TALLOC_CTX *mem_ctx, time_t t) +{ + char *buf; + fstring tempTime; + struct tm *tm = LocalTime(&t); + + if (!tm) + buf = talloc_asprintf(mem_ctx,"%ld seconds since the Epoch",(long)t); + else +#ifndef HAVE_STRFTIME + buf = talloc_strdup(mem_ctx, asctime(tm)); + if(buf[strlen(buf)-1] == '\n') + buf[strlen(buf)-1] = 0; +#else /* !HAVE_STRFTIME */ + strftime(tempTime, sizeof(tempTime)-1, "%a, %d %b %Y %H:%M:%S %Z", tm); + buf = talloc_strdup(mem_ctx, tempTime); +#endif /* !HAVE_STRFTIME */ + return buf; +} + + + +/**************************************************************************** + Return the date and time as a string +****************************************************************************/ + +char *timestring(TALLOC_CTX *mem_ctx, BOOL hires) +{ + char *TimeBuf; + fstring tempTime; + 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) { + TimeBuf = talloc_asprintf(mem_ctx, + "%ld.%06ld seconds since the Epoch", + (long)tp.tv_sec, + (long)tp.tv_usec); + } else { + TimeBuf = talloc_asprintf(mem_ctx, + "%ld seconds since the Epoch", + (long)t); + } + } else { +#ifdef HAVE_STRFTIME + if (hires) { + strftime(tempTime,sizeof(tempTime)-1,"%Y/%m/%d %H:%M:%S",tm); + TimeBuf = talloc_asprintf(mem_ctx, "%s.%06ld", + tempTime, (long)tp.tv_usec); + } else { + strftime(tempTime,100,"%Y/%m/%d %H:%M:%S",tm); + TimeBuf = talloc_strdup(mem_ctx, tempTime); + } +#else + if (hires) { + TimeBuf = talloc_asprintf(mem_ctx, + "%s.%06ld", + asctime(tm), + (long)tp.tv_usec); + } else { + TimeBuf = talloc_strdup(mem_ctx, 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; +} + +/* + return a talloced string representing a NTTIME for human consumption +*/ +const char *nt_time_string(TALLOC_CTX *mem_ctx, const NTTIME *nt) +{ + time_t t = nt_time_to_unix(nt); + return talloc_strdup(mem_ctx, http_timestring(mem_ctx, t)); +} + diff --git a/source4/lib/username.c b/source4/lib/username.c new file mode 100644 index 0000000000..c00ac666d4 --- /dev/null +++ b/source4/lib/username.c @@ -0,0 +1,537 @@ +/* + 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) +{ + 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); +} + + +/**************************************************************************** + * 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(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(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(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; + + 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. +****************************************************************************/ + +static BOOL user_in_netgroup_list(const char *user, const char *ngname) +{ +#ifdef HAVE_NETGROUP + //static char *mydomain = NULL; + /* REWRITE: make thread safe if caching */ + char *mydomain = NULL; + //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); +#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 num_groups; + int i; + gid_t *groups = NULL; + gid_t gid, gid_low, gid_high; + BOOL ret = False; + + *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_winbind_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; + } + + /* + * Get the gid's that this user belongs to. + */ + + if ((num_groups = winbind_getgroups(user, 0, NULL)) == -1) + return False; + + if (num_groups == 0) { + *winbind_answered = True; + return False; + } + + if ((groups = (gid_t *)malloc(sizeof(gid_t) * num_groups )) == NULL) { + DEBUG(0,("user_in_winbind_group_list: malloc fail.\n")); + goto err; + } + + if ((num_groups = winbind_getgroups(user, num_groups, groups)) == -1) { + DEBUG(0,("user_in_winbind_group_list: second winbind_getgroups call \ +failed with error %s\n", strerror(errno) )); + goto err; + } + + /* + * 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. +****************************************************************************/ +static 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; + TALLOC_CTX *mem_ctx; + + 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. + */ + + mem_ctx = talloc_init("smbgroupedit talloc"); + if (!mem_ctx) return -1; + if (pass) { + if (strequal(gname,gidtoname(mem_ctx, pass->pw_gid))) { + DEBUG(10,("user_in_unix_group_list: group %s is primary group.\n", gname )); + goto exit; + } + } + + 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); + goto exit; + } + } + + free_userlist(user_list); + talloc_destroy(mem_ctx); + return False; +exit: + talloc_destroy(mem_ctx); + return True; +} + +/**************************************************************************** + Check if a user is in a group list. Ask winbind first, then use UNIX. +****************************************************************************/ +static 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 */ + if (winbind_lookup_name(domain, groupname, &g_sid, &name_type) && name_type == SID_NAME_DOM_GRP) { + + /* 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/source4/lib/util.c b/source4/lib/util.c new file mode 100644 index 0000000000..64e3dfe88c --- /dev/null +++ b/source4/lib/util.c @@ -0,0 +1,1000 @@ +/* + 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) Anthony Liguori 2003 + Copyright (C) James J Myers 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 + +#else /* !WITH_NISPLUS_HOME */ + +#include "rpcsvc/ypclnt.h" + +#endif /* WITH_NISPLUS_HOME */ +#endif /* HAVE_NETGROUP && WITH_AUTOMOUNT */ + + +/**************************************************************************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;ist_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); +} + +/******************************************************************* + 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 +} + + +/******************************************************************* + Sleep for a specified number of milliseconds. +********************************************************************/ + +void msleep(unsigned int t) +{ + struct timeval tval; + + tval.tv_sec = t/1000; + tval.tv_usec = 1000*(t%1000); + /* this should be the real select - do NOT replace + with sys_select() */ + select(0,NULL,NULL,NULL,&tval); +} + +/**************************************************************************** + Become a daemon, discarding the controlling terminal. +****************************************************************************/ + +void become_daemon(BOOL Fork) +{ + if (Fork) { + if (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 */ +} + + +/**************************************************************************** + 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); +} + +/**************************************************************************** + 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); +} + + +/* + see if a string matches either our primary or one of our secondary + netbios aliases. do a case insensitive match +*/ +BOOL is_myname(const char *name) +{ + const char **aliases; + int i; + + if (strcasecmp(name, lp_netbios_name()) == 0) { + return True; + } + + aliases = lp_netbios_aliases(); + for (i=0; aliases && aliases[i]; i++) { + if (strcasecmp(name, aliases[i]) == 0) { + return True; + } + } + + return False; +} + + +/**************************************************************************** + Get my own name, return in malloc'ed storage. +****************************************************************************/ + +char* get_myname(void) +{ + char *hostname; + const int host_name_max = 255; + char *p; + + hostname = malloc(host_name_max+1); + *hostname = 0; + + /* get my host name */ + if (gethostname(hostname, host_name_max+1) == -1) { + DEBUG(0,("gethostname failed\n")); + return NULL; + } + + /* Ensure null termination. */ + hostname[host_name_max] = '\0'; + + /* split off any parts after an initial . */ + p = strchr_m(hostname,'.'); + + if (p) + *p = 0; + + return hostname; +} + +/**************************************************************************** + Get my own name, including domain. +****************************************************************************/ + +BOOL get_myfullname(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) + fstrcpy(my_name, hostname); + return True; +} + +/**************************************************************************** + Get my own domain name. +****************************************************************************/ + +BOOL get_mydomname(fstring my_domname) +{ + pstring hostname; + char *p; + + *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'; + + p = strchr_m(hostname, '.'); + + if (!p) + return False; + + p++; + + if (my_domname) + fstrcpy(my_domname, p); + + return True; +} + +/**************************************************************************** + Interpret a protocol description string, with a default. +****************************************************************************/ + +int interpret_protocol(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(TALLOC_CTX *mem_ctx, const char *str) +{ + struct in_addr *ret; + uint32 a = interpret_addr(str); + + ret = talloc(mem_ctx, sizeof(struct in_addr)); + if (!ret) return NULL; + 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) +{ + *ip = inet_makeaddr(0,0); + return; +} + + +/******************************************************************* + 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 gid into a group name. +********************************************************************/ + +char *gidtoname(TALLOC_CTX *mem_ctx, gid_t gid) +{ + char *name; + struct group *grp; + + grp = getgrgid(gid); + if (grp) + return(grp->gr_name); + name = talloc_asprintf(mem_ctx, "%d",(int)gid); + return(name); +} + + +/******************************************************************* + 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; +} + +/******************************************************************* + Something really nasty happened - panic ! +********************************************************************/ + +void smb_panic(const char *why) +{ + char *cmd = lp_panic_action(); + int result; + + 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)); + abort(); +} + +/**************************************************************************** + 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 != 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); +} + +/******************************************************************* + Set the remote_arch string based on an enum. This is used in places +where we desperately need to distinguish client type. +********************************************************************/ +void set_remote_arch(struct server_context *smb, enum remote_arch_types type) +{ + const char *arch; + + smb->negotiate.ra_type = type; + switch (type) { + case RA_WFWG: + arch = "WfWg"; + return; + case RA_OS2: + arch = "OS2"; + return; + case RA_WIN95: + arch = "Win95"; + return; + case RA_WINNT: + arch = "WinNT"; + return; + case RA_WIN2K: + arch = "Win2K"; + return; + case RA_WINXP: + arch = "WinXP"; + return; + case RA_SAMBA: + arch = "Samba"; + return; + default: + smb->negotiate.ra_type = RA_UNKNOWN; + arch = "UNKNOWN"; + break; + } + + sub_set_remote_arch(arch); +} + + +void print_asc(int level, const unsigned char *buf,int len) +{ + int i; + for (i=0;i8) 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")); + } +} + +/***************************************************************** + 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) + 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; +} + + +/* + 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(TALLOC_CTX *mem_ctx) +{ + char *myname, *ret; + myname = get_myname(); + ret = talloc_strdup(mem_ctx, myname); + free(myname); + return ret; + +} + +/***************************************************************** + A useful function for returning a path in the Samba lock directory. +*****************************************************************/ + +char *lock_path(TALLOC_CTX* mem_ctx, const char *name) +{ + char *fname; + + fname = talloc_strdup(mem_ctx, lp_lockdir()); + trim_string(fname,"","/"); + + if (!directory_exist(fname,NULL)) + mkdir(fname,0755); + + fname = talloc_asprintf(mem_ctx, "%s/%s", 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 talloc'ed string containing the full path. + **/ + +char *lib_path(TALLOC_CTX* mem_ctx, const char *name) +{ + char *fname; + fname = talloc_asprintf(mem_ctx, "%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; +} + + +/********************************************************* + Recursive routine that is called by unix_wild_match. +*********************************************************/ + +static BOOL unix_do_match(char *regexp, char *str) +{ + 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; +} diff --git a/source4/lib/util_file.c b/source4/lib/util_file.c new file mode 100644 index 0000000000..0eb8046227 --- /dev/null +++ b/source4/lib/util_file.c @@ -0,0 +1,531 @@ +/* + * 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" + +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; +} + + +/************************************************************************* + 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 %d expected %d\n", + fname, s2, 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/source4/lib/util_getent.c b/source4/lib/util_getent.c new file mode 100644 index 0000000000..32641dbf83 --- /dev/null +++ b/source4/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_name = strdup(pwd->pw_gecos)) == NULL) + goto err; + } + if (pwd->pw_dir) { + if ((pent->pw_name = strdup(pwd->pw_dir)) == NULL) + goto err; + } + if (pwd->pw_shell) { + if ((pent->pw_name = 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/source4/lib/util_pw.c b/source4/lib/util_pw.c new file mode 100644 index 0000000000..9d075a05e8 --- /dev/null +++ b/source4/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/source4/lib/util_seaccess.c b/source4/lib/util_seaccess.c new file mode 100644 index 0000000000..eba8cab7fb --- /dev/null +++ b/source4/lib/util_seaccess.c @@ -0,0 +1,486 @@ +/* + 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 if this ACE has a SID in common with the token. +**********************************************************************************/ + +static 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; +} + +/********************************************************************************* + 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; +} + +/* 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, + parent_ctr->owner_sid, + parent_ctr->grp_sid, + parent_ctr->sacl, + new_dacl, &size); + + sdb = make_sec_desc_buf(ctx, size, sd); + + return sdb; +} + +/******************************************************************* + 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, NULL, NULL, NULL, psa, sd_size)) == NULL) + return NT_STATUS_NO_MEMORY; + + return NT_STATUS_OK; +} diff --git a/source4/lib/util_sid.c b/source4/lib/util_sid.c new file mode 100644 index 0000000000..9910a9d261 --- /dev/null +++ b/source4/lib/util_sid.c @@ -0,0 +1,631 @@ +/* + 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_USE_NONE, 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 lp_netbios_name() 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, lp_netbios_name()); + 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_talloc(TALLOC_CTX *mem_ctx, const DOM_SID *sid) +{ + fstring tempSid; + sid_to_string(tempSid, sid); + return talloc_strdup(mem_ctx, tempSid); +} + +/***************************************************************** + 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_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;inum_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; +} + + +/***************************************************************** + Print a GUID structure for debugging. +*****************************************************************/ + +void print_guid(GUID *guid) +{ + int i; + + d_printf("%08x-%04x-%04x", + IVAL(guid->info, 0), SVAL(guid->info, 4), SVAL(guid->info, 6)); + d_printf("-%02x%02x-", guid->info[8], guid->info[9]); + for (i=10;iinfo[i]); + d_printf("\n"); +} diff --git a/source4/lib/util_smbd.c b/source4/lib/util_smbd.c new file mode 100644 index 0000000000..071f20b416 --- /dev/null +++ b/source4/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/source4/lib/util_sock.c b/source4/lib/util_sock.c new file mode 100644 index 0000000000..42dc04f6c8 --- /dev/null +++ b/source4/lib/util_sock.c @@ -0,0 +1,631 @@ +/* + 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" + + +/**************************************************************************** + 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]; + + 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, + struct in_addr *from_addr, int *from_port) +{ + ssize_t ret; + struct sockaddr_in sock; + socklen_t socklen = sizeof(sock); + + 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; + } + + if (from_addr) { + *from_addr = sock.sin_addr; + } + if (from_port) { + *from_port = ntohs(sock.sin_port); + } + + return ret; +} + + +/**************************************************************************** + 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; + + while (total < N) { + ret = sys_read(fd,buffer + total,N - total); + if (ret == 0) { + return 0; + } + if (ret == -1) { + return -1; + } + total += ret; + } + return (ssize_t)total; +} + + +/**************************************************************************** + Write data to a fd. +****************************************************************************/ +ssize_t write_data(int fd, const 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) { + return -1; + } + if (ret == 0) + return total; + + total += ret; + } + return (ssize_t)total; +} + + +/**************************************************************************** +send a keepalive packet (rfc1002) +****************************************************************************/ +BOOL send_nbt_keepalive(int sock_fd) +{ + unsigned char buf[4]; + + buf[0] = SMBkeepalive; + buf[1] = buf[2] = buf[3] = 0; + + return write_data(sock_fd,(char *)buf,4) == 4; +} + + +/**************************************************************************** + 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 ) { + DEBUG(0,("open_socket_in(): socket() call failed: %s\n", strerror(errno))); + return -1; + } + + /* This block sets/clears the SO_REUSEADDR and possibly SO_REUSEPORT. */ + { + int val = rebind ? 1 : 0; + setsockopt(res,SOL_SOCKET,SO_REUSEADDR,(char *)&val,sizeof(val)); +#ifdef SO_REUSEPORT + setsockopt(res,SOL_SOCKET,SO_REUSEPORT,(char *)&val,sizeof(val)); +#endif + } + + /* now we've got a socket - we need to bind it */ + if( bind( res, (struct sockaddr *)&sock, sizeof(sock) ) == -1 ) { + DEBUG(0,("bind failed on port %d - %s\n", port, 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 = 250; /* 250 milliseconds */ + int loops = (timeout) / connect_loop; + + /* create a socket to write to */ + res = socket(PF_INET, type, 0); + if (res == -1) + { DEBUG(0,("socket error\n")); 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) && loops--) { + msleep(connect_loop); + 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; + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_init("open_udp_socket"); + if (!mem_ctx) { + return -1; + } + addr = interpret_addr2(mem_ctx, 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; + + talloc_destroy(mem_ctx); + + if (connect(res,(struct sockaddr *)&sock_out,sizeof(sock_out))) { + close(res); + return -1; + } + + return res; +} + + +/******************************************************************* + 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 (strcasecmp(remotehost, hp->h_name) + && strcasecmp(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_socket_name(TALLOC_CTX *mem_ctx, int fd, BOOL force_lookup) +{ + char *name_buf; + char *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_socket_addr(mem_ctx, fd); + } + + p = get_socket_addr(mem_ctx, fd); + + name_buf = talloc_strdup(mem_ctx, "UNKNOWN"); + if (fd == -1) return name_buf; + + addr_buf = talloc_strdup(mem_ctx, p); + + addr = *interpret_addr2(mem_ctx, 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)); + name_buf = talloc_strdup(mem_ctx, p); + } else { + name_buf = talloc_strdup(mem_ctx, (char *)hp->h_name); + if (!matchname(name_buf, addr)) { + DEBUG(0,("Matchname failed on %s %s\n",name_buf,p)); + name_buf = talloc_strdup(mem_ctx, "UNKNOWN"); + } + } + + alpha_strcpy(name_buf, name_buf, "_-.", sizeof(name_buf)); + if (strstr(name_buf,"..")) { + name_buf = talloc_strdup(mem_ctx, "UNKNOWN"); + } + + return name_buf; +} + +/******************************************************************* + return the IP addr of the remote end of a socket as a string + ******************************************************************/ +char *get_socket_addr(TALLOC_CTX *mem_ctx, int fd) +{ + struct sockaddr sa; + struct sockaddr_in *sockin = (struct sockaddr_in *) (&sa); + int length = sizeof(sa); + char *addr_buf; + + addr_buf = talloc_strdup(mem_ctx, "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; + } + + addr_buf = talloc_strdup(mem_ctx, (char *)inet_ntoa(sockin->sin_addr)); + + return addr_buf; +} + + + +/******************************************************************* +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]; +} + + +/* + determine if a packet is pending for receive on a socket +*/ +BOOL socket_pending(int fd) +{ + fd_set fds; + int selrtn; + struct timeval timeout; + + FD_ZERO(&fds); + FD_SET(fd,&fds); + + /* immediate timeout */ + timeout.tv_sec = 0; + timeout.tv_usec = 0; + + /* yes, this is supposed to be a normal select not a sys_select() */ + selrtn = select(fd+1,&fds,NULL,NULL,&timeout); + + if (selrtn == 1) { + /* the fd is readable */ + return True; + } + + return False; +} diff --git a/source4/lib/util_str.c b/source4/lib/util_str.c new file mode 100644 index 0000000000..19857cff86 --- /dev/null +++ b/source4/lib/util_str.c @@ -0,0 +1,1619 @@ +/* + Unix SMB/CIFS implementation. + Samba utility functions + Copyright (C) Andrew Tridgell 1992-2001 + Copyright (C) Simo Sorce 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" + +/** + * 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) +{ + const 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_m(sep,*s)) + s++; + + /* nothing left? */ + if (! *s) + return(False); + + /* copy over the token */ + for (quoted = False; len < bufsize && *s && (quoted || !strchr_m(sep,*s)); s++) { + if (*s == '\"') { + quoted = !quoted; + } else { + len++; + *buff++ = *s; + } + } + + *ptr = (*s) ? s+1 : s; + *buff = 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 char *last_ptr=NULL; + +BOOL next_token_nr(const char **ptr, char *buff, const char *sep, size_t bufsize) +{ + BOOL ret; + if (!ptr) + ptr = (const char **)&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=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=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. +**/ + +int StrCaseCmp(const char *s, const char *t) +{ + pstring buf1, buf2; + unix_strupper(s, strlen(s)+1, buf1, sizeof(buf1)); + unix_strupper(t, strlen(t)+1, buf2, sizeof(buf2)); + return strcmp(buf1,buf2); +} + +/** + 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_talloc(TALLOC_CTX *mem_ctx, const char *s) +{ + char *str; + + str = talloc_strdup(mem_ctx, s); + strupper(str); + + return str; +} + + +/** + String replace. + NOTE: oldc and newc must be 7 bit characters +**/ + +void string_replace(char *s,char oldc,char newc) +{ + if (strchr(s, oldc)) { + push_ucs2(NULL, tmpbuf,s, sizeof(tmpbuf), STR_TERMINATE); + string_replace_w(tmpbuf, UCS2_CHAR(oldc), UCS2_CHAR(newc)); + pull_ucs2(NULL, s, tmpbuf, strlen(s)+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); +} + +/** + 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) { + memcpy(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; + push_ucs2(NULL, tmpbuf,s, sizeof(tmpbuf), STR_TERMINATE); + for(count=0,ptr=tmpbuf;*ptr;ptr++) + if(*ptr==UCS2_CHAR(c)) + count++; + return(count); +} + +/** +Return True if a string consists only of one particular character. +**/ + +BOOL str_is_all(const char *s,char c) +{ + smb_ucs2_t *ptr; + + if(s == NULL) + return False; + if(!*s) + return False; + + push_ucs2(NULL, tmpbuf,s, sizeof(tmpbuf), STR_TERMINATE); + for(ptr=tmpbuf;*ptr;ptr++) + if(*ptr!=UCS2_CHAR(c)) + return False; + + return True; +} + +/** + Safe string copy into a known length string. maxlength does not + include the terminating zero. +**/ + +char *safe_strcpy(char *dest,const char *src, size_t maxlength) +{ + size_t len; + + if (!dest) { + DEBUG(0,("ERROR: NULL dest in safe_strcpy\n")); + return NULL; + } + +#ifdef DEVELOPER + /* We intentionally write out at the extremity of the destination + * string. If the destination is too short (e.g. pstrcpy into mallocd + * or fstring) then this should cause an error under a memory + * checker. */ + dest[maxlength] = '\0'; + if (PTR_DIFF(&len, dest) > 0) { /* check if destination is on the stack, ok if so */ + log_suspicious_usage("safe_strcpy", src); + } +#endif + + if (!src) { + *dest = 0; + return dest; + } + + len = strlen(src); + + if (len > maxlength) { + DEBUG(0,("ERROR: string overflow by %u (%u - %u) in safe_strcpy [%.50s]\n", + (unsigned int)(len-maxlength), len, 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(char *dest, const char *src, size_t maxlength) +{ + size_t src_len, dest_len; + + if (!dest) { + DEBUG(0,("ERROR: NULL dest in safe_strcat\n")); + return NULL; + } + + if (!src) + return dest; + +#ifdef DEVELOPER + if (PTR_DIFF(&src_len, dest) > 0) { /* check if destination is on the stack, ok if so */ + log_suspicious_usage("safe_strcat", src); + } +#endif + src_len = strlen(src); + dest_len = strlen(dest); + + 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(char *dest, const char *src, const char *other_safe_chars, size_t maxlength) +{ + size_t len, i; + + if (maxlength == 0) { + /* can't fit any bytes at all! */ + return NULL; + } + + if (!dest) { + DEBUG(0,("ERROR: NULL dest in alpha_strcpy\n")); + 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(char *dest,const char *src,size_t n) +{ + char *d = dest; + if (!dest) + return(NULL); + if (!src) { + *dest = 0; + return(dest); + } + while (n-- && (*d++ = *src++)) + ; + *d = 0; + return(dest); +} + +/** + Like strncpy but copies up to the character marker. always null terminates. + returns a pointer to the character marker in the source string (src). +**/ + +char *strncpyn(char *dest, const char *src, size_t n, char c) +{ + char *p; + size_t str_len; + + 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; +} + +/** + 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; +} + +/** + Check if a string is part of a list. +**/ + +BOOL in_list(const char *s, const 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); +} + +/** + 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); + + (*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) 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(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 0) { + 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 + (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(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! +**/ + +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); +} + +/** + Splits out the front and back at a separator. +**/ + +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; + } +} + +/** + 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, 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 *s, char c) +{ + wpstring ws; + pstring s2; + smb_ucs2_t *p; + + 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) +{ + 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)); +} + +/** + Convert a string to lower case. +**/ + +void strlower_m(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) */ + + while (*s && !(((unsigned char)s[0]) & 0x7F)) { + *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) */ + unix_strlower(s,strlen(s)+1,s,strlen(s)+1); +} + +/** + Duplicate convert a string to lower case. +**/ + +char *strdup_lower(const char *s) +{ + char *t = strdup(s); + if (t == NULL) { + DEBUG(0, ("strdup_lower: Out of memory!\n")); + return NULL; + } + strlower_m(t); + return t; +} + +/** + Convert a string to upper case. +**/ + +void strupper_m(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) */ + + while (*s && !(((unsigned char)s[0]) & 0x7F)) { + *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) */ + unix_strupper(s,strlen(s)+1,s,strlen(s)+1); +} + +/** + Convert a string to upper case. +**/ + +char *strdup_upper(const char *s) +{ + char *t = strdup(s); + if (t == NULL) { + DEBUG(0, ("strdup_upper: Out of memory!\n")); + return NULL; + } + strupper_m(t); + return t; +} + +/** + 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> 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= '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++; + } + + /* 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); + data_blob_free(&decoded); + + /* null terminate */ + s[decoded.length] = '\0'; +} + +/** + * 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; +} + +#ifdef VALGRIND +size_t valgrind_strlen(const char *s) +{ + size_t count; + for(count = 0; *s++; count++) + ; + return count; +} +#endif diff --git a/source4/lib/util_unistr.c b/source4/lib/util_unistr.c new file mode 100644 index 0000000000..e5d2b8c3db --- /dev/null +++ b/source4/lib/util_unistr.c @@ -0,0 +1,838 @@ +/* + 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; + + +/******************************************************************* +load the case handling tables +********************************************************************/ +void load_case_tables(void) +{ + static int initialised; + int i; + TALLOC_CTX *mem_ctx; + + if (initialised) return; + initialised = 1; + + mem_ctx = talloc_init("load_case_tables"); + if (!mem_ctx) { + smb_panic("No memory for case_tables"); + } + upcase_table = map_file(lib_path(mem_ctx, "upcase.dat"), 0x20000); + lowcase_table = map_file(lib_path(mem_ctx, "lowcase.dat"), 0x20000); + talloc_destroy(mem_ctx); + + /* 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); + if (!upcase_table) { + smb_panic("No memory for upcase tables"); + } + 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); + if (!lowcase_table) { + smb_panic("No memory for lowcase tables"); + } + 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 +*/ +static int check_dos_char(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)); + if (len1 == 0) return 0; + len2 = convert_string(CH_DOS, CH_UCS2, buf, len1, &c2, 2); + if (len2 != 2) return 0; + return (c == c2); +} + +/** + * Load the valid character map table from valid.dat 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; + TALLOC_CTX *mem_ctx; + + if (mapped_file) { + /* Can't unmap files, so stick with what we have */ + return; + } + + mem_ctx = talloc_init("init_valid_table"); + if (!mem_ctx) { + smb_panic("No memory for valid_table"); + } + valid_file = map_file(lib_path(mem_ctx, "valid.dat"), 0x10000); + talloc_destroy(mem_ctx); + 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); + if (!valid_table) { + smb_panic("No memory for valid_table"); + } + 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) 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); +} + +/******************************************************************* + 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(TALLOC_CTX *mem_ctx, const UNISTR2 *str) +{ + pstring ret; + unistr2_to_ascii(ret, str, sizeof(ret)); + return talloc_strdup(mem_ctx, 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; SVAL(src,0); len++, src++) ; + + 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; (len < max) && SVAL(src, 0); len++, src++) ; + + return len; +} + +/******************************************************************* +wide strchr() +********************************************************************/ +const smb_ucs2_t *strchr_w(const smb_ucs2_t *s, smb_ucs2_t c) +{ + while (*s != 0) { + if (c == *s) return s; + s++; + } + if (c == *s) return s; + + return NULL; +} + +const smb_ucs2_t *strchr_wa(const smb_ucs2_t *s, char c) +{ + return strchr_w(s, UCS2_CHAR(c)); +} + +const 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 p; + } while (p-- != s); + return NULL; +} + +/******************************************************************* +wide strstr() +********************************************************************/ +const smb_ucs2_t *strstr_w(const smb_ucs2_t *s, const smb_ucs2_t *ins) +{ + const smb_ucs2_t *r; + size_t slen, inslen; + + if (!s || !*s || !ins || !*ins) return NULL; + slen = strlen_w(s); + inslen = strlen_w(ins); + r = 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; +} + +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;ibuffer) { + dst->buffer = (uint16*) talloc(ctx, sizeof(uint16) * (len + 1)); + if (!dst->buffer) return NULL; + } + + /* set UNISTR2 parameters */ + dst->uni_max_len = len + 1; + dst->undoc = 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/source4/lib/util_uuid.c b/source4/lib/util_uuid.c new file mode 100644 index 0000000000..76eb93a9b8 --- /dev/null +++ b/source4/lib/util_uuid.c @@ -0,0 +1,104 @@ +/* + * Unix SMB/CIFS implementation. + * UUID server routines + * Copyright (C) Theodore Ts'o 1996, 1997, + * Copyright (C) Jim McDonough 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" + +/* + * Offset between 15-Oct-1582 and 1-Jan-70 + */ +#define TIME_OFFSET_HIGH 0x01B21DD2 +#define TIME_OFFSET_LOW 0x13814000 + +struct uuid { + uint32 time_low; + uint16 time_mid; + uint16 time_hi_and_version; + uint8 clock_seq[2]; + uint8 node[6]; +}; + + +static void uuid_pack(const struct uuid *uu, GUID *ptr) +{ + uint8 *out = ptr->info; + + SIVAL(out, 0, uu->time_low); + SSVAL(out, 4, uu->time_mid); + SSVAL(out, 6, uu->time_hi_and_version); + memcpy(out+8, uu->clock_seq, 2); + memcpy(out+10, uu->node, 6); +} + +static void uuid_unpack(const GUID in, struct uuid *uu) +{ + const uint8 *ptr = in.info; + + uu->time_low = IVAL(ptr, 0); + uu->time_mid = SVAL(ptr, 4); + uu->time_hi_and_version = SVAL(ptr, 6); + memcpy(uu->clock_seq, ptr+8, 2); + memcpy(uu->node, ptr+10, 6); +} + +void uuid_generate_random(GUID *out) +{ + GUID tmp; + struct uuid uu; + + generate_random_buffer(tmp.info, sizeof(tmp.info), True); + 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; + uuid_pack(&uu, out); +} + +char *guid_to_string(const GUID in) +{ + struct uuid uu; + char *out; + + uuid_unpack(in, &uu); + + 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 *uuid_string(TALLOC_CTX *mem_ctx, const GUID in) +{ + struct uuid uu; + char *out; + + uuid_unpack(in, &uu); + if (!out) return NULL; + out = talloc_asprintf(mem_ctx, + "%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; +} diff --git a/source4/lib/wins_srv.c b/source4/lib/wins_srv.c new file mode 100644 index 0000000000..30e81b08ab --- /dev/null +++ b/source4/lib/wins_srv.c @@ -0,0 +1,361 @@ +/* + 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; + + if (asprintf(&keystr, WINS_SRV_FMT, inet_ntoa(wins_ip), + inet_ntoa(src_ip)) == -1) { + DEBUG(0, ("wins_srv_is_dead: malloc error\n")); + return NULL; + } + + 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(TALLOC_CTX *mem_ctx, struct tagged_ip *ip, const char *str) +{ + char *s = strchr(str, ':'); + if (!s) { + fstrcpy(ip->tag, "*"); + ip->ip = *interpret_addr2(mem_ctx, str); + return; + } + + ip->ip = *interpret_addr2(mem_ctx, 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; + TALLOC_CTX *mem_ctx; + + 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; + + mem_ctx = talloc_init("wins_ssrv_tags"); + if (!mem_ctx) { + 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(mem_ctx, &t_ip, list[i]); + + /* see if we already have it */ + for (j=0;jbufused) 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/source4/libads/.cvsignore b/source4/libads/.cvsignore new file mode 100644 index 0000000000..5f2a5c4cf7 --- /dev/null +++ b/source4/libads/.cvsignore @@ -0,0 +1,2 @@ +*.po +*.po32 diff --git a/source4/libads/ads_ldap.c b/source4/libads/ads_ldap.c new file mode 100644 index 0000000000..97f12de0f7 --- /dev/null +++ b/source4/libads/ads_ldap.c @@ -0,0 +1,155 @@ +/* + 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 *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(&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, exp, attrs); + free(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 *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(&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, 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(exp); + SAFE_FREE(sidstr); + + return status; +} + +#endif diff --git a/source4/libads/ads_status.c b/source4/libads/ads_status.c new file mode 100644 index 0000000000..80fdb99eac --- /dev/null +++ b/source4/libads/ads_status.c @@ -0,0 +1,133 @@ +/* + 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 == ADS_ERROR_NT) { + DEBUG(0,("don't use ads_build_error with ADS_ERROR_NT!\n")); + ret.err.rc = -1; + ret.error_type = 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 != ADS_ERROR_NT) { + DEBUG(0,("don't use ads_build_nt_error without ADS_ERROR_NT!\n")); + ret.err.rc = -1; + ret.error_type = 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 == ADS_ERROR_NT){ + return status.err.nt_status; + } +#ifdef HAVE_LDAP + if ((status.error_type == ADS_ERROR_LDAP) + && (status.err.rc == LDAP_NO_MEMORY)) { + return NT_STATUS_NO_MEMORY; + } +#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) +{ + int msg_ctx; + static char *ret; + + SAFE_FREE(ret); + msg_ctx = 0; + + switch (status.error_type) { + case ADS_ERROR_SYSTEM: + return strerror(status.err.rc); +#ifdef HAVE_LDAP + case ADS_ERROR_LDAP: + return ldap_err2string(status.err.rc); +#endif +#ifdef HAVE_KRB5 + case ADS_ERROR_KRB5: + return error_message(status.err.rc); +#endif +#ifdef HAVE_GSSAPI + case 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 ADS_ERROR_NT: + return nt_errstr(ads_ntstatus(status)); + default: + return "Unknown ADS error type!? (not compiled in?)"; + } + +} + + diff --git a/source4/libads/ads_struct.c b/source4/libads/ads_struct.c new file mode 100644 index 0000000000..652bfe31be --- /dev/null +++ b/source4/libads/ads_struct.c @@ -0,0 +1,141 @@ +/* + 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 to know if we can + use lp_ads_server() */ + if (realm && *realm && strcasecmp(lp_realm(), realm) != 0) { + ads->server.foreign = 1; + } + if (workgroup && *workgroup && strcasecmp(lp_workgroup(), workgroup) != 0) { + ads->server.foreign = 1; + } + + return ads; +} + +/* a simpler ads_init() interface using all defaults */ +ADS_STRUCT *ads_init_simple(void) +{ + return ads_init(NULL, NULL, NULL); +} + +/* + free the memory used by the ADS structure initialized with 'ads_init(...)' +*/ +void ads_destroy(ADS_STRUCT **ads) +{ + if (ads && *ads) { +#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); + SAFE_FREE(*ads); + } +} diff --git a/source4/libads/ads_utils.c b/source4/libads/ads_utils.c new file mode 100644 index 0000000000..626c177926 --- /dev/null +++ b/source4/libads/ads_utils.c @@ -0,0 +1,181 @@ +/* + 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; +} + +/* +translated the GROUP_CTRL Flags to GroupType (groupType) +*/ +uint32 ads_gcb2gtype(uint16 gcb) +{ + uint32 gtype = 0x00000000; + + if (gcb & GCB_ALIAS_GROUP) gtype |= GTYPE_SECURITY_BUILTIN_LOCAL_GROUP; + else if(gcb & GCB_LOCAL_GROUP) gtype |= GTYPE_SECURITY_DOMAIN_LOCAL_GROUP; + if (gcb & GCB_GLOBAL_GROUP) gtype |= GTYPE_SECURITY_GLOBAL_GROUP; + + return gtype; +} + +/* +translated the GroupType (groupType) to GROUP_CTRL Flags +*/ +uint16 ads_gtype2gcb(uint32 gtype) +{ + uint16 gcb = 0x0000; + + switch(gtype) { + case GTYPE_SECURITY_BUILTIN_LOCAL_GROUP: + gcb = GCB_ALIAS_GROUP; + break; + case GTYPE_SECURITY_DOMAIN_LOCAL_GROUP: + gcb = GCB_LOCAL_GROUP; + break; + case GTYPE_SECURITY_GLOBAL_GROUP: + gcb = GCB_GLOBAL_GROUP; + break; + + case GTYPE_DISTRIBUTION_GLOBAL_GROUP: + gcb = GCB_GLOBAL_GROUP; + break; + case GTYPE_DISTRIBUTION_DOMAIN_LOCAL_GROUP: + gcb = GCB_LOCAL_GROUP; + break; + case GTYPE_DISTRIBUTION_UNIVERSAL_GROUP: + gcb = GCB_GLOBAL_GROUP; + break; + } + + return gcb; +} + +/* +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_ACCOUNT: + return SID_NAME_USER; + default: + DEBUG(1,("hmm, need to map account type 0x%x\n", atype)); + } + return SID_NAME_UNKNOWN; +} diff --git a/source4/libads/disp_sec.c b/source4/libads/disp_sec.c new file mode 100644 index 0000000000..c9de447e69 --- /dev/null +++ b/source4/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/source4/libads/kerberos.c b/source4/libads/kerberos.c new file mode 100644 index 0000000000..bef2febaef --- /dev/null +++ b/source4/libads/kerberos.c @@ -0,0 +1,140 @@ +/* + 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) +{ + 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; + } + + 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); + + if (ret) { + DEBUG(0,("kerberos_kinit_password %s failed: %s\n", + s, error_message(ret))); + } + free(s); + return ret; +} + + +#endif diff --git a/source4/libads/kerberos_verify.c b/source4/libads/kerberos_verify.c new file mode 100644 index 0000000000..9367f53539 --- /dev/null +++ b/source4/libads/kerberos_verify.c @@ -0,0 +1,173 @@ +/* + Unix SMB/CIFS implementation. + kerberos utility library + Copyright (C) Andrew Tridgell 2001 + Copyright (C) Remus Koos 2001 + 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" + +#ifdef HAVE_KRB5 + +/* + verify an incoming ticket and parse out the principal name and + authorization_data if available +*/ +NTSTATUS ads_verify_ticket(ADS_STRUCT *ads, const DATA_BLOB *ticket, + char **principal, DATA_BLOB *auth_data, + DATA_BLOB *ap_rep, + uint8 session_key[16]) +{ + krb5_context context; + krb5_auth_context auth_context = NULL; + krb5_keytab keytab = NULL; + krb5_data packet; + krb5_ticket *tkt = NULL; + int ret, i; + krb5_keyblock * key; + krb5_principal host_princ; + char *host_princ_s; + fstring myname; + char *password_s; + krb5_data password; + krb5_enctype *enctypes = NULL; + BOOL auth_ok = False; + + if (!secrets_init()) { + DEBUG(1,("secrets_init failed\n")); + return NT_STATUS_LOGON_FAILURE; + } + + password_s = secrets_fetch_machine_password(); + if (!password_s) { + DEBUG(1,("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,("krb5_init_context failed (%s)\n", error_message(ret))); + return NT_STATUS_LOGON_FAILURE; + } + + ret = krb5_set_default_realm(context, ads->auth.realm); + if (ret) { + DEBUG(1,("krb5_set_default_realm failed (%s)\n", error_message(ret))); + return NT_STATUS_LOGON_FAILURE; + } + + /* 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,("krb5_auth_con_init failed (%s)\n", error_message(ret))); + return NT_STATUS_LOGON_FAILURE; + } + + fstrcpy(myname, lp_netbios_name()); + strlower(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,("krb5_parse_name(%s) failed (%s)\n", host_princ_s, error_message(ret))); + return NT_STATUS_LOGON_FAILURE; + } + + if (!(key = (krb5_keyblock *)malloc(sizeof(*key)))) { + return NT_STATUS_NO_MEMORY; + } + + if ((ret = get_kerberos_allowed_etypes(context, &enctypes))) { + DEBUG(1,("krb5_get_permitted_enctypes failed (%s)\n", + error_message(ret))); + return NT_STATUS_LOGON_FAILURE; + } + + /* we need to setup a auth context with each possible encoding type in turn */ + for (i=0;enctypes[i];i++) { + if (create_kerberos_key_from_string(context, host_princ, &password, key, enctypes[i])) { + continue; + } + + krb5_auth_con_setuseruserkey(context, auth_context, key); + + packet.length = ticket->length; + packet.data = (krb5_pointer)ticket->data; + + if (!(ret = krb5_rd_req(context, &auth_context, &packet, + NULL, keytab, NULL, &tkt))) { + free_kerberos_etypes(context, enctypes); + auth_ok = True; + break; + } + } + + if (!auth_ok) { + DEBUG(3,("krb5_rd_req with auth failed (%s)\n", + error_message(ret))); + return NT_STATUS_LOGON_FAILURE; + } + + ret = krb5_mk_rep(context, auth_context, &packet); + if (ret) { + DEBUG(3,("Failed to generate mutual authentication reply (%s)\n", + error_message(ret))); + krb5_auth_con_free(context, auth_context); + return NT_STATUS_LOGON_FAILURE; + } + + *ap_rep = data_blob(packet.data, packet.length); + free(packet.data); + + krb5_get_smb_session_key(context, auth_context, session_key); + DEBUG(0,("SMB session key (from ticket) follows:\n")); + dump_data(0, session_key, 16); + +#if 0 + file_save("/tmp/ticket.dat", ticket->data, ticket->length); +#endif + + get_auth_data_from_tkt(auth_data, tkt); + +#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,("krb5_unparse_name failed (%s)\n", + error_message(ret))); + data_blob_free(auth_data); + data_blob_free(ap_rep); + krb5_auth_con_free(context, auth_context); + return NT_STATUS_LOGON_FAILURE; + } + + krb5_auth_con_free(context, auth_context); + + return NT_STATUS_OK; +} + +#endif /* HAVE_KRB5 */ diff --git a/source4/libads/krb5_setpw.c b/source4/libads/krb5_setpw.c new file mode 100644 index 0000000000..9d8fb8d24c --- /dev/null +++ b/source4/libads/krb5_setpw.c @@ -0,0 +1,701 @@ +/* + 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 = 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 krb5_error_code krb5_setpw_result_code_string(krb5_context context, + int result_code, + char **code_string) +{ + switch (result_code) { + case KRB5_KPASSWD_MALFORMED: + *code_string = "Malformed request error"; + break; + case KRB5_KPASSWD_HARDERROR: + *code_string = "Server error"; + break; + case KRB5_KPASSWD_AUTHERROR: + *code_string = "Authentication error"; + break; + case KRB5_KPASSWD_SOFTERROR: + *code_string = "Password change rejected"; + break; + case KRB5_KPASSWD_ACCESSDENIED: + *code_string = "Client does not have proper authorization"; + break; + case KRB5_KPASSWD_BAD_VERSION: + *code_string = "Protocol version not supported"; + break; + case KRB5_KPASSWD_INITIAL_FLAG_NEEDED: + *code_string = "Authorization ticket must have initial flag set"; + break; + case KRB5_KPASSWD_POLICY_REJECT: + *code_string = "Password rejected due to policy requirements"; + break; + case KRB5_KPASSWD_BAD_PRINCIPAL: + *code_string = "Target principal does not exist"; + break; + case KRB5_KPASSWD_ETYPE_NOSUPP: + *code_string = "Unsupported encryption type"; + break; + default: + *code_string = "Password change failed"; + break; + } + + 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 { + char *errstr; + krb5_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 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, creds.server); + 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; +} + +ADS_STATUS 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))) { + 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 krb5_chg_password(kpasswd_server, target_principal, + auth_password, new_password, time_offset); + else + return 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 *hostname, + const char *password) +{ + ADS_STATUS status; + char *host = strdup(hostname); + char *principal; + + strlower(host); + + /* + we need to use the '$' form of the name here, as otherwise the + server might end up setting the password for a user instead + */ + asprintf(&principal, "%s$@%s", host, ads->auth.realm); + + status = krb5_set_password(ads->auth.kdc_server, principal, password, ads->auth.time_offset); + + free(host); + free(principal); + + return status; +} + + + +#endif diff --git a/source4/libads/ldap.c b/source4/libads/ldap.c new file mode 100644 index 0000000000..0bcd76cb9f --- /dev/null +++ b/source4/libads/ldap.c @@ -0,0 +1,2098 @@ +/* + Unix SMB/CIFS implementation. + ads (active directory) utility library + Copyright (C) Andrew Tridgell 2001 + Copyright (C) Remus Koos 2001 + Copyright (C) Jim McDonough 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 + */ +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; +} + +/* used by the IP comparison function */ +struct ldap_ip { + struct in_addr ip; + unsigned port; +}; + +/* compare 2 ldap IPs by nearness to our interfaces - used in qsort */ +static int ldap_ip_compare(struct ldap_ip *ip1, struct ldap_ip *ip2) +{ + return ip_compare(&ip1->ip, &ip2->ip); +} + +/* try connecting to a ldap server via DNS */ +static BOOL ads_try_dns(ADS_STRUCT *ads) +{ + const char *c_realm; + const char *ptr; + char *realm; + char *list = NULL; + pstring tok; + struct ldap_ip *ip_list; + int count, i=0; + + c_realm = ads->server.realm; + if (!c_realm || !*c_realm) { + c_realm = lp_realm(); + } + if (!c_realm || !*c_realm) { + c_realm = ads->server.workgroup; + } + if (!c_realm || !*c_realm) { + c_realm = lp_workgroup(); + } + if (!c_realm) { + return False; + } + realm = smb_xstrdup(c_realm); + + DEBUG(6,("ads_try_dns: looking for realm '%s'\n", realm)); + if (ldap_domain2hostlist(realm, &list) != LDAP_SUCCESS) { + SAFE_FREE(realm); + return False; + } + + DEBUG(6,("ads_try_dns: ldap realm '%s' host list '%s'\n", realm, list)); + SAFE_FREE(realm); + + count = count_chars(list, ' ') + 1; + ip_list = malloc(count * sizeof(struct ldap_ip)); + if (!ip_list) { + 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); + } + ip_list[i].ip = *interpret_addr2(tok); + ip_list[i].port = port; + if (!is_zero_ip(ip_list[i].ip)) { + i++; + } + } + free(list); + + count = i; + + /* we sort the list of addresses by closeness to our interfaces. This + tries to prevent us using a DC on the other side of the country */ + if (count > 1) { + qsort(ip_list, count, sizeof(struct ldap_ip), + QSORT_CAST ldap_ip_compare); + } + + for (i=0;iserver.workgroup; + BOOL list_ordered; + + if (!workgroup) { + workgroup = lp_workgroup(); + } + + DEBUG(6,("ads_try_netbios: looking for workgroup '%s'\n", workgroup)); + + /* try the PDC first */ + if (get_pdc_ip(workgroup, &pdc_ip)) { + DEBUG(6,("ads_try_netbios: trying server '%s'\n", + inet_ntoa(pdc_ip))); + if (ads_try_connect(ads, inet_ntoa(pdc_ip), LDAP_PORT)) + return True; + } + + /* now any DC, including backups */ + if (get_dc_list(workgroup, &ip_list, &count, &list_ordered)) { + for (i=0;ilast_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; + } + + /* try with a smb.conf ads server setting if we are connecting + to the primary workgroup or realm */ + if (!ads->server.foreign && + ads_try_connect(ads, lp_ads_server(), LDAP_PORT)) { + goto got_connection; + } + + /* try via DNS */ + if (ads_try_dns(ads)) { + goto got_connection; + } + + /* try via netbios lookups */ + if (!lp_disable_netbios() && ads_try_netbios(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, lp_netbios_name()); + strlower(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_BASE | LDAP_ONE | LDAP_SUBTREE) + * @param exp 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 *exp, + const char **attrs, void **res, + int *count, void **cookie) +{ + int rc, i, version; + char *utf8_exp, *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_exp, exp) == (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_exp, + 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", exp, 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_BASE | LDAP_ONE | LDAP_SUBTREE) + * @param exp 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 *exp, + const char **attrs, void **res) +{ + void *cookie = NULL; + int count = 0; + ADS_STATUS status; + + status = ads_do_paged_search(ads, bind_path, scope, exp, 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, exp, + 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_BASE | LDAP_ONE | LDAP_SUBTREE) + * @param exp 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 *exp, 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, exp, 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, exp, 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_BASE | LDAP_ONE | LDAP_SUBTREE) + * @param exp 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 *exp, + const char **attrs, void **res) +{ + struct timeval timeout; + int rc; + char *utf8_exp, *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_exp, exp) == (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_exp, + 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 exp Search expression + * @param attrs Attributes to retrieve + * @return status of search + **/ +ADS_STATUS ads_search(ADS_STRUCT *ads, void **res, + const char *exp, + const char **attrs) +{ + return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE, + exp, 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 res Search results + * @return dn string + **/ +char *ads_get_dn(ADS_STRUCT *ads, void *res) +{ + char *utf8_dn, *unix_dn; + + utf8_dn = ldap_get_dn(ads->ld, res); + pull_utf8_allocate((void **) &unix_dn, utf8_dn); + 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 *exp; + const char *attrs[] = {"*", "nTSecurityDescriptor", NULL}; + + /* the easiest way to find a machine account anywhere in the tree + is to look for hostname$ */ + if (asprintf(&exp, "(samAccountName=%s$)", host) == -1) { + DEBUG(1, ("asprintf failed!\n")); + return ADS_ERROR_NT(NT_STATUS_NO_MEMORY); + } + + status = ads_search(ads, res, exp, attrs); + free(exp); + 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(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 || strcasecmp(org_unit, "Computers") == 0) { + 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, + 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; + + 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(&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(&psp2[5]); + servicePrincipalName[3] = psp2; + + free(ou_str); + if (!new_dn) + goto done; + + if (!(samAccountName = talloc_asprintf(ctx, "%s$", hostname))) + goto done; + + acct_control = UF_WORKSTATION_TRUST_ACCOUNT | 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; + + ads_mod_str(ctx, &mods, "cn", hostname); + ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName); + ads_mod_strlist(ctx, &mods, "objectClass", objectClass); + ads_mod_str(ctx, &mods, "userPrincipalName", host_upn); + ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName); + ads_mod_str(ctx, &mods, "dNSHostName", hostname); + ads_mod_str(ctx, &mods, "userAccountControl", controlstr); + ads_mod_str(ctx, &mods, "operatingSystem", "Samba"); + ads_mod_str(ctx, &mods, "operatingSystemVersion", VERSION); + + ret = ads_gen_add(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. + */ + 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; jbv_len; j++) { + printf("%02X", (unsigned char)values[i]->bv_val[j]); + } + printf("\n"); + } +} + +struct uuid { + uint32 i1; + uint16 i2; + uint16 i3; + uint8 s[8]; +}; + +static void dump_guid(const char *field, struct berval **values) +{ + int i; + GUID guid; + TALLOC_CTX *mem_ctx; + mem_ctx = talloc_init("dump_guid"); + if (!mem_ctx) return; + for (i=0; values[i]; i++) { + memcpy(guid.info, values[i]->bv_val, sizeof(guid.info)); + printf("%s: %s\n", field, uuid_string(mem_ctx, guid)); + } + talloc_destroy(mem_ctx); +} + +/* + 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, const char *org_unit) +{ + ADS_STATUS status; + LDAPMessage *res; + char *host; + + /* hostname must be lowercase */ + host = strdup(hostname); + strlower(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, 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; + char *hostnameDN, *host; + int rc; + + /* hostname must be lowercase */ + host = strdup(hostname); + strlower(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; + } + + hostnameDN = ads_get_dn(ads, (LDAPMessage *)res); + 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 *exp = 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(&exp, "(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, exp, 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) +{ + char **values; + char **ret = NULL; + int i, n; + + values = ldap_get_values(ads->ld, msg, field); + if (!values) + return NULL; + + for (i=0;values[i];i++) + /* noop */ ; + n = i; + + ret = talloc(mem_ctx, sizeof(char *) * (n+1)); + if (!ret) { + ldap_value_free(values); + return NULL; + } + + for (i=0;ild, 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, GUID *guid) +{ + char **values; + + values = ldap_get_values(ads->ld, msg, "objectGUID"); + if (!values) + return False; + + if (values[0]) { + memcpy(guid, values[0], sizeof(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(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 list of trusted domains + * @param ads connection to ads server + * @param mem_ctx TALLOC_CTX for allocating results + * @param num_trusts pointer to number of trusts + * @param names pointer to trusted domain name list + * @param sids pointer to list of sids of trusted domains + * @return the count of SIDs pulled + **/ +ADS_STATUS ads_trusted_domains(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, + int *num_trusts, + char ***names, + char ***alt_names, + DOM_SID **sids) +{ + const char *attrs[] = {"name", "flatname", "securityIdentifier", + "trustDirection", NULL}; + ADS_STATUS status; + void *res, *msg; + int count, i; + + *num_trusts = 0; + + status = ads_search(ads, &res, "(objectcategory=trustedDomain)", attrs); + if (!ADS_ERR_OK(status)) return status; + + count = ads_count_replies(ads, res); + if (count == 0) { + ads_msgfree(ads, res); + return ADS_ERROR(LDAP_NO_RESULTS_RETURNED); + } + + (*names) = talloc(mem_ctx, sizeof(char *) * count); + (*alt_names) = talloc(mem_ctx, sizeof(char *) * count); + (*sids) = talloc(mem_ctx, sizeof(DOM_SID) * count); + if (! *names || ! *sids) return ADS_ERROR(LDAP_NO_MEMORY); + + for (i=0, msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) { + uint32 direction; + + /* direction is a 2 bit bitfield, 1 means they trust us + but we don't trust them, so we should not list them + as users from that domain can't login */ + if (ads_pull_uint32(ads, msg, "trustDirection", &direction) && + direction == 1) { + continue; + } + + (*names)[i] = ads_pull_string(ads, mem_ctx, msg, "name"); + (*alt_names)[i] = ads_pull_string(ads, mem_ctx, msg, "flatname"); + + if ((*alt_names)[i] && (*alt_names)[i][0]) { + /* we prefer the flatname as the primary name + for consistency with RPC */ + char *name = (*alt_names)[i]; + (*alt_names)[i] = (*names)[i]; + (*names)[i] = name; + } + if (ads_pull_sid(ads, msg, "securityIdentifier", &(*sids)[i])) { + i++; + } + } + + ads_msgfree(ads, res); + + *num_trusts = i; + + 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(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, char **workgroup) +{ + char *exp; + ADS_STATUS rc; + char **principles; + char *prefix; + int prefix_length; + int i; + void *res; + const char *attrs[] = {"servicePrincipalName", NULL}; + + (*workgroup) = NULL; + + asprintf(&exp, "(&(objectclass=computer)(dnshostname=%s.%s))", + ads->config.ldap_server_name, ads->config.realm); + rc = ads_search(ads, &res, exp, attrs); + free(exp); + + if (!ADS_ERR_OK(rc)) { + return rc; + } + + principles = ads_pull_strings(ads, mem_ctx, res, "servicePrincipalName"); + + 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 (strncasecmp(principles[i], prefix, prefix_length) == 0 && + strcasecmp(ads->config.realm, principles[i]+prefix_length) != 0 && + !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/source4/libads/ldap_printer.c b/source4/libads/ldap_printer.c new file mode 100644 index 0000000000..f5cd4f2885 --- /dev/null +++ b/source4/libads/ldap_printer.c @@ -0,0 +1,341 @@ +/* + Unix SMB/CIFS implementation. + ads (active directory) printer utility library + Copyright (C) Jim McDonough 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, *exp; + 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(&exp, "(cn=%s-%s)", srv_cn[0], printer); + status = ads_search(ads, res, exp, attrs); + + ldap_memfree(srv_dn); + ldap_value_free(srv_cn); + free(exp); + return status; +} + +/* + 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/source4/libads/ldap_user.c b/source4/libads/ldap_user.c new file mode 100644 index 0000000000..7efe5338f3 --- /dev/null +++ b/source4/libads/ldap_user.c @@ -0,0 +1,119 @@ +/* + Unix SMB/CIFS implementation. + ads (active directory) utility library + Copyright (C) Jim McDonough 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 *exp; + const char *attrs[] = {"*", NULL}; + char *escaped_user = escape_ldap_string_alloc(user); + if (!escaped_user) { + return ADS_ERROR(LDAP_NO_MEMORY); + } + + asprintf(&exp, "(samAccountName=%s)", escaped_user); + status = ads_search(ads, res, exp, attrs); + SAFE_FREE(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/source4/libads/ldap_utils.c b/source4/libads/ldap_utils.c new file mode 100644 index 0000000000..907f7c8aff --- /dev/null +++ b/source4/libads/ldap_utils.c @@ -0,0 +1,96 @@ +/* + 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 *exp, + const char **attrs, void **res) +{ + ADS_STATUS status; + int count = 3; + char *bp; + + 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--) { + status = ads_do_search_all(ads, bp, scope, exp, attrs, res); + if (ADS_ERR_OK(status)) { + DEBUG(5,("Search for %s gave %d replies\n", + exp, ads_count_replies(ads, *res))); + 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); + free(bp); + return status; + } + } + free(bp); + + 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 *exp, + const char **attrs) +{ + return ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE, + exp, 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/source4/libads/sasl.c b/source4/libads/sasl.c new file mode 100644 index 0000000000..c33255bf56 --- /dev/null +++ b/source4/libads/sasl.c @@ -0,0 +1,427 @@ +/* + 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 = 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, + lp_netbios_name(), + 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 = 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; + int rc; + + blob = spnego_gen_negTokenTarg(principal, ads->auth.time_offset); + + if (!blob.data) { + return ADS_ERROR(LDAP_OPERATIONS_ERROR); + } + + /* now send the auth packet and we should be done */ + cred.bv_val = 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); + + 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); + +#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; + if (ads_kinit_password(ads) == 0) { + status = ads_sasl_spnego_krb5_bind(ads, principal); + } + if (ADS_ERR_OK(status)) + 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) +{ + int 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; + OM_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, + &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(p, strlen(ads->config.bind_path)+4, "dn:%s", ads->config.bind_path); + p += strlen(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, &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/source4/libads/util.c b/source4/libads/util.c new file mode 100644 index 0000000000..335cabc952 --- /dev/null +++ b/source4/libads/util.c @@ -0,0 +1,60 @@ +/* + 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; + + if ((password = secrets_fetch_machine_password()) == 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)) { + 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/source4/libcli/.cvsignore b/source4/libcli/.cvsignore new file mode 100644 index 0000000000..2588860f65 --- /dev/null +++ b/source4/libcli/.cvsignore @@ -0,0 +1,3 @@ +*.po +*.po32 + diff --git a/source4/libcli/cliconnect.c b/source4/libcli/cliconnect.c new file mode 100644 index 0000000000..da8a842dae --- /dev/null +++ b/source4/libcli/cliconnect.c @@ -0,0 +1,207 @@ +/* + Unix SMB/CIFS implementation. + client connect/disconnect routines + 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" + +/* + wrapper around cli_sock_connect() +*/ +BOOL cli_socket_connect(struct cli_state *cli, const char *server, struct in_addr *ip) +{ + struct cli_socket *sock; + + sock = cli_sock_init(); + if (!sock) return False; + + if (!cli_sock_connect_byname(sock, server, 0)) { + cli_sock_close(sock); + return False; + } + + cli->transport = cli_transport_init(sock); + if (!cli->transport) { + cli_sock_close(sock); + return False; + } + + return True; +} + +/* wrapper around cli_transport_connect() */ +BOOL cli_transport_establish(struct cli_state *cli, + struct nmb_name *calling, + struct nmb_name *called) +{ + return cli_transport_connect(cli->transport, calling, called); +} + +/* wrapper around smb_raw_negotiate() */ +BOOL cli_negprot(struct cli_state *cli) +{ + NTSTATUS status; + status = smb_raw_negotiate(cli->transport); + return NT_STATUS_IS_OK(status); +} + +/* wrapper around smb_raw_session_setup() */ +BOOL cli_session_setup(struct cli_state *cli, + const char *user, + const char *password, + const char *domain) +{ + union smb_sesssetup setup; + NTSTATUS status; + TALLOC_CTX *mem_ctx; + + cli->session = cli_session_init(cli->transport); + if (!cli->session) return False; + + mem_ctx = talloc_init("cli_session_setup"); + if (!mem_ctx) return False; + + setup.generic.level = RAW_SESSSETUP_GENERIC; + setup.generic.in.sesskey = cli->transport->negotiate.sesskey; + setup.generic.in.capabilities = CAP_UNICODE | CAP_STATUS32 | + CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS | + CAP_W2K_SMBS | CAP_LARGE_READX | CAP_LARGE_WRITEX; + setup.generic.in.password = password; + setup.generic.in.user = user; + setup.generic.in.domain = domain; + + status = smb_raw_session_setup(cli->session, mem_ctx, &setup); + + cli->session->vuid = setup.generic.out.vuid; + + talloc_destroy(mem_ctx); + + return NT_STATUS_IS_OK(status); +} + +/* wrapper around smb_tree_connect() */ +BOOL cli_send_tconX(struct cli_state *cli, const char *sharename, const char *devtype, + const char *password) +{ + union smb_tcon tcon; + TALLOC_CTX *mem_ctx; + NTSTATUS status; + + cli->tree = cli_tree_init(cli->session); + if (!cli->tree) return False; + + cli->tree->reference_count++; + + /* setup a tree connect */ + tcon.generic.level = RAW_TCON_TCONX; + tcon.tconx.in.flags = 0; + tcon.tconx.in.password = data_blob(password, strlen(password)+1); + tcon.tconx.in.path = sharename; + tcon.tconx.in.device = devtype; + + mem_ctx = talloc_init("tcon"); + if (!mem_ctx) { + return False; + } + + status = smb_tree_connect(cli->tree, mem_ctx, &tcon); + + cli->tree->tid = tcon.tconx.out.cnum; + + talloc_destroy(mem_ctx); + + return NT_STATUS_IS_OK(status); +} + + +/* + easy way to get to a fully connected cli_state in one call +*/ +NTSTATUS cli_full_connection(struct cli_state **ret_cli, + const char *myname, + const char *host, + struct in_addr *ip, + const char *sharename, + const char *devtype, + const char *username, + const char *domain, + const char *password, + uint_t flags, + BOOL *retry) +{ + struct cli_tree *tree; + NTSTATUS status; + + *ret_cli = NULL; + + status = cli_tree_full_connection(&tree, myname, host, 0, sharename, devtype, + username, domain, password); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + (*ret_cli) = cli_state_init(); + + (*ret_cli)->tree = tree; + (*ret_cli)->session = tree->session; + (*ret_cli)->transport = tree->session->transport; + tree->reference_count++; + + return status; +} + + +/* + disconnect the tree +*/ +BOOL cli_tdis(struct cli_state *cli) +{ + NTSTATUS status; + status = smb_tree_disconnect(cli->tree); + return NT_STATUS_IS_OK(status); +} + +/**************************************************************************** + Initialise a client state structure. +****************************************************************************/ +struct cli_state *cli_state_init(void) +{ + struct cli_state *cli; + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_init("cli_state"); + if (!mem_ctx) return NULL; + + cli = talloc_zero(mem_ctx, sizeof(*cli)); + cli->mem_ctx = mem_ctx; + + return cli; +} + +/**************************************************************************** + Shutdown a client structure. +****************************************************************************/ +void cli_shutdown(struct cli_state *cli) +{ + if (!cli) return; + cli->tree->reference_count++; + cli_tree_close(cli->tree); + if (cli->mem_ctx) { + talloc_destroy(cli->mem_ctx); + } +} diff --git a/source4/libcli/clideltree.c b/source4/libcli/clideltree.c new file mode 100644 index 0000000000..8769b8dfa7 --- /dev/null +++ b/source4/libcli/clideltree.c @@ -0,0 +1,117 @@ +/* + Unix SMB/CIFS implementation. + useful function for deleting a whole directory tree + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 delete_state { + struct cli_state *cli; + int total_deleted; + BOOL failed; +}; + +/* + callback function for torture_deltree() +*/ +static void delete_fn(file_info *finfo, const char *name, void *state) +{ + struct delete_state *dstate = state; + char *s, *n; + if (strcmp(finfo->name, ".") == 0 || + strcmp(finfo->name, "..") == 0) return; + + n = strdup(name); + n[strlen(n)-1] = 0; + asprintf(&s, "%s%s", n, finfo->name); + + if (finfo->mode & FILE_ATTRIBUTE_READONLY) { + if (!cli_setatr(dstate->cli, s, 0, 0)) { + DEBUG(2,("Failed to remove READONLY on %s - %s\n", + s, cli_errstr(dstate->cli))); + } + } + + if (finfo->mode & FILE_ATTRIBUTE_DIRECTORY) { + char *s2; + asprintf(&s2, "%s\\*", s); + cli_unlink(dstate->cli, s2); + cli_list(dstate->cli, s2, + FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM, + delete_fn, state); + free(s2); + if (!cli_rmdir(dstate->cli, s)) { + DEBUG(2,("Failed to delete %s - %s\n", + s, cli_errstr(dstate->cli))); + dstate->failed = True; + } + dstate->total_deleted++; + } else { + if (!cli_unlink(dstate->cli, s)) { + DEBUG(2,("Failed to delete %s - %s\n", + s, cli_errstr(dstate->cli))); + dstate->failed = True; + } + dstate->total_deleted++; + } + free(s); + free(n); +} + +/* + recursively descend a tree deleting all files + returns the number of files deleted, or -1 on error +*/ +int cli_deltree(struct cli_state *cli, const char *dname) +{ + char *mask; + struct delete_state dstate; + + dstate.cli = cli; + dstate.total_deleted = 0; + dstate.failed = False; + + /* it might be a file */ + if (cli_unlink(cli, dname)) { + return 1; + } + if (NT_STATUS_EQUAL(cli_nt_error(cli), NT_STATUS_OBJECT_NAME_NOT_FOUND) || + NT_STATUS_EQUAL(cli_nt_error(cli), NT_STATUS_OBJECT_PATH_NOT_FOUND) || + NT_STATUS_EQUAL(cli_nt_error(cli), NT_STATUS_NO_SUCH_FILE)) { + return 0; + } + + asprintf(&mask, "%s\\*", dname); + cli_unlink(cli, mask); + cli_list(dstate.cli, mask, + FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM, + delete_fn, &dstate); + free(mask); + if (!cli_rmdir(dstate.cli, dname)) { + DEBUG(2,("Failed to delete %s - %s\n", + dname, cli_errstr(dstate.cli))); + return -1; + } + dstate.total_deleted++; + + if (dstate.failed) { + return -1; + } + + return dstate.total_deleted; +} diff --git a/source4/libcli/clidfs.c b/source4/libcli/clidfs.c new file mode 100644 index 0000000000..fc24cccf54 --- /dev/null +++ b/source4/libcli/clidfs.c @@ -0,0 +1,558 @@ +/* + Unix SMB/CIFS implementation. + Dfs routines + Copyright (C) James Myers 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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_client_initialize(struct cli_client* context, + const char* sockops, + char* username, char* password, char* workgroup, + int flags) +{ + int i; + for (i=0; i < DFS_MAX_CLUSTER_SIZE ; i++) { + context->cli[i] = cli_raw_initialise(); + } + context->sockops = sockops; + context->username = username; + context->password = password; + context->workgroup = workgroup; + context->connection_flags = flags; + if (flags & CLI_FULL_CONNECTION_USE_DFS) + context->use_dfs = True; + context->number_members = DFS_MAX_CLUSTER_SIZE; + return True; +} + +/**************************************************************************** + Interpret a Dfs referral structure. + The length of the structure is returned + The structure of a Dfs referral depends on the info level. +****************************************************************************/ + +static int interpret_referral(struct cli_state *cli, + int level,char *p,referral_info *rinfo) +{ + char* q; + int version, size; + + version = SVAL(p,0); + size = SVAL(p,2); + rinfo->server_type = SVAL(p,4); + rinfo->referral_flags = SVAL(p,6); + rinfo->proximity = SVAL(p,8); + rinfo->ttl = SVAL(p,10); + rinfo->pathOffset = SVAL(p,12); + rinfo->altPathOffset = SVAL(p,14); + rinfo->nodeOffset = SVAL(p,16); + DEBUG(3,("referral version=%d, size=%d, server_type=%d, flags=0x%x, proximity=%d, ttl=%d, pathOffset=%d, altPathOffset=%d, nodeOffset=%d\n", + version, size, rinfo->server_type, rinfo->referral_flags, + rinfo->proximity, rinfo->ttl, rinfo->pathOffset, + rinfo->altPathOffset, rinfo->nodeOffset)); + + q = (char*)(p + (rinfo->pathOffset)); + //printf("p=%p, q=%p, offset=%d\n", p, q, rinfo->pathOffset); + //printf("hex=0x%x, string=%s\n", q, q); + clistr_pull(cli, rinfo->path, q, + sizeof(rinfo->path), + -1, STR_TERMINATE); + DEBUG(4,("referral path=%s\n", rinfo->path)); + q = (char*)(p + (rinfo->altPathOffset)/sizeof(char)); + if (rinfo->altPathOffset > 0) + clistr_pull(cli, rinfo->altPath, q, + sizeof(rinfo->altPath), + -1, STR_TERMINATE); + DEBUG(4,("referral alt path=%s\n", rinfo->altPath)); + q = (char*)(p + (rinfo->nodeOffset)/sizeof(char)); + if (rinfo->nodeOffset > 0) + clistr_pull(cli, rinfo->node, q, + sizeof(rinfo->node), + -1, STR_TERMINATE); + DEBUG(4,("referral node=%s\n", rinfo->node)); + fstrcpy(rinfo->host, &rinfo->node[1]); + p = strchr_m(&rinfo->host[1],'\\'); + if (!p) { + printf("invalid referral node %s\n", rinfo->node); + return -1; + } + *p = 0; + rinfo->share = talloc_strdup(cli->mem_ctx, p+1); + DEBUG(3,("referral host=%s share=%s\n", + rinfo->host, rinfo->share)); + return size; +} + +#if 0 +int cli_select_dfs_referral(struct cli_state *cli, dfs_info* dinfo) +{ + return (int)sys_random()%dinfo->number_referrals; +} + +int cli_get_dfs_referral(struct cli_state *cli,const char *Fname, dfs_info* dinfo) +{ + struct smb_trans2 parms; + int info_level; + char *p; + pstring fname; + int i; + char *rparam=NULL, *rdata=NULL; + int param_len, data_len; + uint16 setup; + pstring param; + DATA_BLOB trans_param, trans_data; + + /* NT uses 260, OS/2 uses 2. Both accept 1. */ + info_level = (cli->capabilities&CAP_NT_SMBS)?260:1; + + pstrcpy(fname,Fname); + + setup = TRANSACT2_GET_DFS_REFERRAL ; + SSVAL(param,0,CLI_DFS_MAX_REFERRAL_LEVEL); /* attribute */ + p = param+2; + p += clistr_push(cli, param+2, fname, -1, + STR_TERMINATE); + + param_len = PTR_DIFF(p, param); + DEBUG(3,("cli_get_dfs_referral: sending request\n")); + + trans_param.length = param_len; + trans_param.data = param; + trans_data.length = 0; + trans_data.data = NULL; + + if (!cli_send_trans(cli, SMBtrans2, + NULL, /* Name */ + -1, 0, /* fid, flags */ + &setup, 1, 0, /* setup, length, max */ + &trans_param, 10, /* param, length, max */ + &trans_data, + cli->max_xmit /* data, length, max */ + )) { + return 0; + } + + if (!cli_receive_trans(cli, SMBtrans2, + &rparam, ¶m_len, + &rdata, &data_len) && + cli_is_dos_error(cli)) { + return 0; + } + //printf("cli_get_dfs_referral: received response, rdata=%p, rparam=%p\n", + // rdata, rparam); + + if (cli_is_error(cli) || !rdata) + return 0; + + /* parse out some important return info */ + //printf("cli_get_dfs_referral: valid response\n"); + p = rdata; + dinfo->path_consumed = SVAL(p,0); + dinfo->number_referrals = SVAL(p,2); + dinfo->referral_flags = SVAL(p,4); + DEBUG(3,("cli_get_dfs_referral: path_consumed=%d, # referrals=%d, flags=0x%x\n", + dinfo->path_consumed, dinfo->number_referrals, + dinfo->referral_flags)); + + /* point to the referral bytes */ + p+=8; + for (i=0; i < dinfo->number_referrals; i++) { + p += interpret_referral(cli,info_level,p,&dinfo->referrals[i]); + } + + SAFE_FREE(rdata); + SAFE_FREE(rparam); + + DEBUG(3,("received %d Dfs referrals\n", + dinfo->number_referrals)); + + dinfo->selected_referral = cli_select_dfs_referral(cli, dinfo); + DEBUG(3, ("selected Dfs referral %d %s\n", + dinfo->selected_referral, dinfo->referrals[dinfo->selected_referral].node)); + + return(dinfo->number_referrals); +} +#endif + +/* check if the server produced Dfs redirect */ +BOOL cli_check_dfs_redirect(struct cli_state* c, char* fname, + dfs_info* dinfo) +{ + //printf("check_dfs_redirect: error %s\n", + // cli_errstr(c)); + if (cli_is_dos_error(c)) { + printf("got dos error\n"); + return False; + + } else { + NTSTATUS status; + + /* Check NT error */ + + status = cli_nt_error(c); + //printf("got nt error 0x%x\n", status); + + if (NT_STATUS_V(NT_STATUS_PATH_NOT_COVERED) != NT_STATUS_V(status)) { + return False; + } + } + /* execute trans2 getdfsreferral */ + //printf("check_dfs_redirect: process referral\n"); + //cli_get_dfs_referral(c, fname, dinfo); + return True; +} + +int cli_dfs_open_connection(struct cli_client* cluster, + char* host, char* share, int flags) +{ + int i; + BOOL retry; + struct cli_state* c; + + // check if already connected + for (i=0; i < DFS_MAX_CLUSTER_SIZE; i++) { + if (cluster->cli[i]->in_use && strequal(host, cli_state_get_host(cluster->cli[i])) + && strequal(share, cli_state_get_share(cluster->cli[i]))) { + DEBUG(3,("cli_dfs_open_connection: already connected to \\\\%s\\%s\n", host, share)); + return i; + } + } + // open connection + DEBUG(3,("cli_dfs_open_connection: opening \\\\%s\\%s %s@%s\n", + host, share, cluster->username, cluster->workgroup)); + for (i=0; i < DFS_MAX_CLUSTER_SIZE; i++) { + if (!cluster->cli[i]->in_use) { + break; + } + } + if (i >= DFS_MAX_CLUSTER_SIZE) + return -1; + + c = cluster->cli[i]; + if (NT_STATUS_IS_ERR(cli_full_connection(&c, + NULL, host, NULL, 0, + share, "?????", + cluster->username, cluster->workgroup, + cluster->password, flags, + &retry))) + return -1; + cli_state_set_sockopt(cluster->cli[i], cluster->sockops); + cli_state_set_host(cluster->cli[i], host); + cli_state_set_share(cluster->cli[i], share); + cluster->cli[i]->in_use = True; + DEBUG(3,("cli_dfs_open_connection: connected \\\\%s\\%s (%d) %s@%s\n", + cli_state_get_host(cluster->cli[i]), cli_state_get_share(cluster->cli[i]), i, + cluster->username, cluster->workgroup)); + + return i; +} + +/********************************************************************** + Parse the pathname of the form \hostname\service\reqpath + into the dfs_path structure + **********************************************************************/ + +BOOL cli_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_string(temp,"\\","\\"); + DEBUG(10,("temp in cli_parse_dfs_path: .%s. after trimming \\'s\n",temp)); + + /* now tokenize */ + /* parse out hostname */ + p = strchr(temp,'\\'); + if(p == NULL) + return False; + *p = '\0'; + pstrcpy(pdp->hostname,temp); + DEBUG(10,("hostname: %s\n",pdp->hostname)); + + /* parse out servicename */ + temp = p+1; + p = strchr(temp,'\\'); + if(p == NULL) { + pstrcpy(pdp->servicename,temp); + pdp->reqpath[0] = '\0'; + return True; + } + *p = '\0'; + pstrcpy(pdp->servicename,temp); + DEBUG(10,("servicename: %s\n",pdp->servicename)); + + /* rest is reqpath */ + pstrcpy(pdp->reqpath, p+1); + + DEBUG(10,("rest of the path: %s\n",pdp->reqpath)); + return True; +} + +char* rebuild_filename(char *referral_fname, struct cli_state* c, + char* fname, int path_consumed) +{ + const char *template = "\\\\%s\\%s\\%s"; + struct dfs_path dp; + + // TODO: handle consumed length + DEBUG(3,("rebuild_filename: %s, %d consumed of %d\n", + fname, path_consumed, strlen(fname))); + if (cli_parse_dfs_path(fname, &dp)) { + DEBUG(3,("rebuild_filename: reqpath=%s\n", + dp.reqpath)); + asprintf(&referral_fname, + template, cli_state_get_host(c), + cli_state_get_share(c), dp.reqpath); + } + else + return NULL; + DEBUG(3,("rebuild_filename: %s -> %s\n", fname, referral_fname)); + return referral_fname; +} + +/**************************************************************************** + Open a file (allowing for Dfs referral). +****************************************************************************/ + +int cli_dfs_open(struct cli_client* cluster, int *server, + char *fname_src, int flags, int share_mode) +{ + int referral_number; + dfs_info dinfo; + char *referral_fname; + int fnum; + + DEBUG(3,("cli_dfs_open: open %s on server %s(%d)\n", + fname_src, cli_state_get_host(cluster->cli[*server]), *server)); + cluster->cli[*server]->dfs_referral = *server; + if ((fnum = cli_open(cluster->cli[*server], fname_src, flags, share_mode)) < 0) { + if (cli_check_dfs_redirect(cluster->cli[*server], fname_src, &dinfo)) { + // choose referral, check if already connected, open if not + referral_number = dinfo.selected_referral; + DEBUG(3,("cli_dfs_open: redirecting to %s\n", dinfo.referrals[referral_number].node)); + cluster->cli[*server]->dfs_referral = cli_dfs_open_connection(cluster, + dinfo.referrals[referral_number].host, + dinfo.referrals[referral_number].share, + cluster->connection_flags); + *server = cluster->cli[*server]->dfs_referral; + if (server < 0) + return False; + // rebuild file name and retry operation. + if (rebuild_filename(referral_fname, cluster->cli[*server], fname_src, dinfo.path_consumed) == NULL) + return False; + fname_src = referral_fname; + DEBUG(3,("cli_dfs_open: Dfs open %s on server %s(%d)\n", + fname_src, cli_state_get_host(cluster->cli[*server]), *server)); + fnum = cli_open(cluster->cli[*server], fname_src, flags, share_mode); + } + if (cli_is_error(cluster->cli[*server])) { + printf("cli_dfs_open: open of %s failed (%s)\n", + fname_src, cli_errstr(cluster->cli[*server])); + return -1; + } + } + DEBUG(3,("cli_dfs_open: open %s fnum=%d\n", + fname_src, fnum)); + return fnum; +} + +/**************************************************************************** + Delete a file (allowing for Dfs referral). +****************************************************************************/ + +NTSTATUS cli_nt_unlink(struct cli_client* cluster, int *server, + char *fname_src, uint16 FileAttributes) +{ + int referral_number; + dfs_info dinfo; + char *referral_fname; + struct smb_unlink parms; + + DEBUG(3,("cli_nt_unlink: delete %s on server %s(%d), attributes=0x%x\n", + fname_src, cli_state_get_host(cluster->cli[*server]), *server, + FileAttributes)); + cluster->cli[*server]->dfs_referral = *server; + parms.in.pattern = fname_src; + parms.in.dirtype = FileAttributes; + if (NT_STATUS_IS_ERR(cli_raw_unlink(cluster->cli[*server], &parms))) { + printf("cli_nt_unlink: delete of %s failed (%s)\n", + fname_src, cli_errstr(cluster->cli[*server])); + if (cli_check_dfs_redirect(cluster->cli[*server], fname_src, &dinfo)) { + // choose referral, check if already connected, open if not + referral_number = dinfo.selected_referral; + DEBUG(3,("cli_nt_unlink: redirecting to %s\n", dinfo.referrals[referral_number].node)); + cluster->cli[*server]->dfs_referral = cli_dfs_open_connection(cluster, + dinfo.referrals[referral_number].host, + dinfo.referrals[referral_number].share, + cluster->connection_flags); + *server = cluster->cli[*server]->dfs_referral; + if (server < 0) + return NT_STATUS_INTERNAL_ERROR; + // rebuild file name and retry operation. + if (rebuild_filename(referral_fname, cluster->cli[*server], fname_src, dinfo.path_consumed) == NULL) + return NT_STATUS_INTERNAL_ERROR; + fname_src = referral_fname; + DEBUG(3,("cli_nt_unlink: Dfs delete %s on server %s(%d)\n", + fname_src, cli_state_get_host(cluster->cli[*server]), *server)); + cli_raw_unlink(cluster->cli[*server], &parms); + } + if (cli_is_error(cluster->cli[*server])) { + printf("cli_nt_unlink: delete of %s failed (%s)\n", + fname_src, cli_errstr(cluster->cli[*server])); + } + } + return cli_nt_error(cluster->cli[*server]); +} + +/**************************************************************************** + Rename a file (allowing for Dfs referral). +****************************************************************************/ + +BOOL cli_dfs_rename(struct cli_client* cluster, int *server, + char *fname_src, char *fname_dst) +{ + int referral_number; + dfs_info dinfo; + char *referral_fname; + + DEBUG(3,("cli_dfs_rename: rename %s to %s on server %s(%d)\n", + fname_src, fname_dst, cli_state_get_host(cluster->cli[*server]), *server)); + cluster->cli[*server]->dfs_referral = *server; + if (!cli_rename(cluster->cli[*server], fname_src, fname_dst)) { + if (cli_check_dfs_redirect(cluster->cli[*server], fname_src, &dinfo)) { + // choose referral, check if already connected, open if not + referral_number = dinfo.selected_referral; + DEBUG(3,("cli_dfs_rename: redirecting to %s\n", dinfo.referrals[referral_number].node)); + cluster->cli[*server]->dfs_referral = cli_dfs_open_connection(cluster, + dinfo.referrals[referral_number].host, + dinfo.referrals[referral_number].share, + cluster->connection_flags); + *server = cluster->cli[*server]->dfs_referral; + if (server < 0) + return False; + // rebuild file name and retry operation. + if (rebuild_filename(referral_fname, cluster->cli[*server], fname_src, dinfo.path_consumed) == NULL) + return False; + fname_src = referral_fname; + DEBUG(3,("cli_dfs_rename: Dfs rename %s to %s on server %s(%d)\n", + fname_src, fname_dst, cli_state_get_host(cluster->cli[*server]), *server)); + cli_rename(cluster->cli[*server], fname_src, fname_dst); + } + if (cli_is_error(cluster->cli[*server])) { + printf("cli_dfs_rename: rename of %s to %s failed (%s)\n", + fname_src, fname_dst, cli_errstr(cluster->cli[*server])); + return False; + } + } + return True; +} + +/**************************************************************************** + Make directory (allowing for Dfs referral). +****************************************************************************/ + +BOOL cli_dfs_mkdir(struct cli_client* cluster, int *server, + char *fname_src) +{ + int referral_number; + dfs_info dinfo; + char *referral_fname; + + DEBUG(3,("cli_dfs_mkdir: mkdir %s on server %s(%d)\n", + fname_src, cli_state_get_host(cluster->cli[*server]), *server)); + cluster->cli[*server]->dfs_referral = *server; + if (!cli_mkdir(cluster->cli[*server], fname_src)) { + printf("cli_dfs_mkdir: mkdir of %s failed (%s)\n", + fname_src, cli_errstr(cluster->cli[*server])); + if (cli_check_dfs_redirect(cluster->cli[*server], fname_src, &dinfo)) { + // choose referral, check if already connected, open if not + referral_number = dinfo.selected_referral; + DEBUG(3,("cli_dfs_mkdir: redirecting to %s\n", dinfo.referrals[referral_number].node)); + cluster->cli[*server]->dfs_referral = cli_dfs_open_connection(cluster, + dinfo.referrals[referral_number].host, + dinfo.referrals[referral_number].share, + cluster->connection_flags); + *server = cluster->cli[*server]->dfs_referral; + if (server < 0) + return False; + // rebuild file name and retry operation. + if (rebuild_filename(referral_fname, cluster->cli[*server], fname_src, dinfo.path_consumed) == NULL) + return False; + fname_src = referral_fname; + DEBUG(3,("cli_dfs_mkdir: Dfs mkdir %s on server %s(%d)\n", + fname_src, cli_state_get_host(cluster->cli[*server]), *server)); + cli_mkdir(cluster->cli[*server], fname_src); + } + if (cli_is_error(cluster->cli[*server])) { + printf("cli_dfs_mkdir: mkdir of %s failed (%s)\n", + fname_src, cli_errstr(cluster->cli[*server])); + return False; + } + } + return True; +} + +/**************************************************************************** + Remove directory (allowing for Dfs referral). +****************************************************************************/ + +BOOL cli_dfs_rmdir(struct cli_client* cluster, int *server, + char *fname_src) +{ + int referral_number; + dfs_info dinfo; + char *referral_fname; + + DEBUG(3,("cli_dfs_rmdir: rmdir %s on server %s(%d)\n", + fname_src, cli_state_get_host(cluster->cli[*server]), *server)); + cluster->cli[*server]->dfs_referral = *server; + if (!cli_rmdir(cluster->cli[*server], fname_src)) { + printf("cli_dfs_rmdir: rmdir of %s failed (%s)\n", + fname_src, cli_errstr(cluster->cli[*server])); + if (cli_check_dfs_redirect(cluster->cli[*server], fname_src, &dinfo)) { + // choose referral, check if already connected, open if not + referral_number = dinfo.selected_referral; + DEBUG(3,("cli_dfs_rmdir: redirecting to %s\n", dinfo.referrals[referral_number].node)); + cluster->cli[*server]->dfs_referral = cli_dfs_open_connection(cluster, + dinfo.referrals[referral_number].host, + dinfo.referrals[referral_number].share, + cluster->connection_flags); + *server = cluster->cli[*server]->dfs_referral; + if (server < 0) + return False; + // rebuild file name and retry operation. + if (rebuild_filename(referral_fname, cluster->cli[*server], fname_src, dinfo.path_consumed) == NULL) + return False; + fname_src = referral_fname; + DEBUG(3,("cli_dfs_rmdir: Dfs rmdir %s on server %s(%d)\n", + fname_src, cli_state_get_host(cluster->cli[*server]), *server)); + cli_rmdir(cluster->cli[*server], fname_src); + } + if (cli_is_error(cluster->cli[*server])) { + printf("cli_dfs_rmdir: rmdir of %s failed (%s)\n", + fname_src, cli_errstr(cluster->cli[*server])); + return False; + } + } + return True; +} diff --git a/source4/libcli/clifile.c b/source4/libcli/clifile.c new file mode 100644 index 0000000000..c203e4633d --- /dev/null +++ b/source4/libcli/clifile.c @@ -0,0 +1,647 @@ +/* + Unix SMB/CIFS implementation. + client file operations + Copyright (C) Andrew Tridgell 1994-1998 + Copyright (C) Jeremy Allison 2001-2002 + Copyright (C) James Myers 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#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) +{ + union smb_setfileinfo parms; + NTSTATUS status; + + if (hard_link) { + parms.generic.level = SMB_SFILEINFO_UNIX_HLINK; + parms.unix_hlink.file.fname = fname_src; + parms.unix_hlink.in.link_dest = fname_dst; + } else { + parms.generic.level = SMB_SFILEINFO_UNIX_LINK; + parms.unix_link.file.fname = fname_src; + parms.unix_link.in.link_dest = fname_dst; + } + + status = smb_raw_setpathinfo(cli->tree, &parms); + + return NT_STATUS_IS_OK(status); +} + +/**************************************************************************** + Map standard UNIX permissions onto wire representations. +****************************************************************************/ +static 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) +{ + union smb_setfileinfo parms; + NTSTATUS status; + + parms.generic.level = SMB_SFILEINFO_UNIX_BASIC; + parms.unix_basic.file.fname = fname; + parms.unix_basic.in.uid = uid; + parms.unix_basic.in.gid = gid; + parms.unix_basic.in.mode = mode; + + status = smb_raw_setpathinfo(cli->tree, &parms); + + return NT_STATUS_IS_OK(status); +} + +/**************************************************************************** + 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) +{ + struct smb_rename parms; + + parms.in.attrib = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_DIRECTORY; + parms.in.pattern1 = fname_src; + parms.in.pattern2 = fname_dst; + return NT_STATUS_IS_OK(smb_raw_rename(cli->tree, &parms)); +} + + +/**************************************************************************** + Delete a file. +****************************************************************************/ +BOOL cli_unlink(struct cli_state *cli, const char *fname) +{ + struct smb_unlink parms; + + parms.in.pattern = fname; + if (strchr(fname, '*')) { + parms.in.attrib = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN; + } else { + parms.in.attrib = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_DIRECTORY; + } + return NT_STATUS_IS_OK(smb_raw_unlink(cli->tree, &parms)); +} + +/**************************************************************************** + Create a directory. +****************************************************************************/ +BOOL cli_mkdir(struct cli_state *cli, const char *dname) +{ + union smb_mkdir parms; + + parms.mkdir.level = RAW_MKDIR_MKDIR; + parms.mkdir.in.path = dname; + + return NT_STATUS_IS_OK(smb_raw_mkdir(cli->tree, &parms)); +} + + +/**************************************************************************** + Remove a directory. +****************************************************************************/ +BOOL cli_rmdir(struct cli_state *cli, const char *dname) +{ + struct smb_rmdir parms; + + parms.in.path = dname; + return NT_STATUS_IS_OK(smb_raw_rmdir(cli->tree, &parms)); +} + + +/**************************************************************************** + Set or clear the delete on close flag. +****************************************************************************/ +BOOL cli_nt_delete_on_close(struct cli_state *cli, int fnum, BOOL flag) +{ + union smb_setfileinfo parms; + NTSTATUS status; + + parms.disposition_info.level = RAW_SFILEINFO_DISPOSITION_INFO; + parms.disposition_info.file.fnum = fnum; + parms.disposition_info.in.delete_on_close = flag; + + status = smb_raw_setfileinfo(cli->tree, &parms); + + return NT_STATUS_IS_OK(status); +} + + +/**************************************************************************** + Create/open a file - exposing the full horror of the NT API :-). + Used in CIFS-on-CIFS NTVFS. +****************************************************************************/ +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 SecurityFlags) +{ + union smb_open open_parms; + TALLOC_CTX *mem_ctx; + NTSTATUS status; + + mem_ctx = talloc_init("raw_open"); + if (!mem_ctx) return -1; + + open_parms.ntcreatex.level = RAW_OPEN_NTCREATEX; + open_parms.ntcreatex.in.flags = CreatFlags; + open_parms.ntcreatex.in.root_fid = 0; + open_parms.ntcreatex.in.access_mask = DesiredAccess; + open_parms.ntcreatex.in.file_attr = FileAttributes; + open_parms.ntcreatex.in.alloc_size = 0; + open_parms.ntcreatex.in.share_access = ShareAccess; + open_parms.ntcreatex.in.open_disposition = CreateDisposition; + open_parms.ntcreatex.in.create_options = CreateOptions; + open_parms.ntcreatex.in.impersonation = 0; + open_parms.ntcreatex.in.security_flags = SecurityFlags; + open_parms.ntcreatex.in.fname = fname; + + status = smb_raw_open(cli->tree, mem_ctx, &open_parms); + talloc_destroy(mem_ctx); + + if (NT_STATUS_IS_OK(status)) { + return open_parms.ntcreatex.out.fnum; + } + + return -1; +} + + +/**************************************************************************** + Open a file (using SMBopenx) + 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) +{ + union smb_open open_parms; + unsigned openfn=0; + unsigned accessmode=0; + TALLOC_CTX *mem_ctx; + NTSTATUS status; + + mem_ctx = talloc_init("raw_open"); + if (!mem_ctx) return -1; + + if (flags & O_CREAT) { + openfn |= OPENX_OPEN_FUNC_CREATE; + } + if (!(flags & O_EXCL)) { + if (flags & O_TRUNC) { + openfn |= OPENX_OPEN_FUNC_TRUNC; + } else { + openfn |= OPENX_OPEN_FUNC_OPEN; + } + } + + accessmode = (share_mode<tree, mem_ctx, &open_parms); + talloc_destroy(mem_ctx); + + if (NT_STATUS_IS_OK(status)) { + return open_parms.openx.out.fnum; + } + + return -1; +} + + +/**************************************************************************** + Close a file. +****************************************************************************/ +BOOL cli_close(struct cli_state *cli, int fnum) +{ + union smb_close close_parms; + NTSTATUS status; + + close_parms.close.level = RAW_CLOSE_CLOSE; + close_parms.close.in.fnum = fnum; + close_parms.close.in.write_time = 0; + status = smb_raw_close(cli->tree, &close_parms); + return NT_STATUS_IS_OK(status); +} + +/**************************************************************************** + 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) +{ + union smb_lock parms; + struct smb_lock_entry lock[1]; + NTSTATUS status; + + parms.lockx.level = RAW_LOCK_LOCKX; + parms.lockx.in.fnum = fnum; + parms.lockx.in.mode = locktype; + parms.lockx.in.timeout = timeout; + parms.lockx.in.ulock_cnt = 0; + parms.lockx.in.lock_cnt = 1; + lock[0].pid = cli->session->pid; + lock[0].offset = offset; + lock[0].count = len; + parms.lockx.in.locks = &lock[0]; + + status = smb_raw_lock(cli->tree, &parms); + + return status; +} + + +/**************************************************************************** + Lock a file. +****************************************************************************/ +BOOL cli_lock(struct cli_state *cli, int fnum, + uint32 offset, uint32 len, int timeout, enum brl_type lock_type) +{ + union smb_lock parms; + struct smb_lock_entry lock[1]; + NTSTATUS status; + + parms.lockx.level = RAW_LOCK_LOCKX; + parms.lockx.in.fnum = fnum; + parms.lockx.in.mode = (lock_type == READ_LOCK? 1 : 0); + parms.lockx.in.timeout = timeout; + parms.lockx.in.ulock_cnt = 0; + parms.lockx.in.lock_cnt = 1; + lock[0].pid = cli->session->pid; + lock[0].offset = offset; + lock[0].count = len; + parms.lockx.in.locks = &lock[0]; + + status = smb_raw_lock(cli->tree, &parms); + + return NT_STATUS_IS_OK(status); +} + + +/**************************************************************************** + Unlock a file. +****************************************************************************/ +BOOL cli_unlock(struct cli_state *cli, int fnum, uint32 offset, uint32 len) +{ + union smb_lock parms; + struct smb_lock_entry lock[1]; + NTSTATUS status; + + parms.lockx.level = RAW_LOCK_LOCKX; + parms.lockx.in.fnum = fnum; + parms.lockx.in.mode = 0; + parms.lockx.in.timeout = 0; + parms.lockx.in.ulock_cnt = 1; + parms.lockx.in.lock_cnt = 0; + lock[0].pid = cli->session->pid; + lock[0].offset = offset; + lock[0].count = len; + parms.lockx.in.locks = &lock[0]; + + status = smb_raw_lock(cli->tree, &parms); + return NT_STATUS_IS_OK(status); +} + + +/**************************************************************************** + Lock a file with 64 bit offsets. +****************************************************************************/ +BOOL cli_lock64(struct cli_state *cli, int fnum, + SMB_OFF_T offset, SMB_OFF_T len, int timeout, enum brl_type lock_type) +{ + union smb_lock parms; + int ltype; + struct smb_lock_entry lock[1]; + NTSTATUS status; + + if (!(cli->transport->negotiate.capabilities & CAP_LARGE_FILES)) { + return cli_lock(cli, fnum, offset, len, timeout, lock_type); + } + + parms.lockx.level = RAW_LOCK_LOCKX; + parms.lockx.in.fnum = fnum; + + ltype = (lock_type == READ_LOCK? 1 : 0); + ltype |= LOCKING_ANDX_LARGE_FILES; + parms.lockx.in.mode = ltype; + parms.lockx.in.timeout = timeout; + parms.lockx.in.ulock_cnt = 0; + parms.lockx.in.lock_cnt = 1; + lock[0].pid = cli->session->pid; + lock[0].offset = offset; + lock[0].count = len; + parms.lockx.in.locks = &lock[0]; + + status = smb_raw_lock(cli->tree, &parms); + + return NT_STATUS_IS_OK(status); +} + + +/**************************************************************************** + Unlock a file with 64 bit offsets. +****************************************************************************/ +BOOL cli_unlock64(struct cli_state *cli, int fnum, SMB_OFF_T offset, SMB_OFF_T len) +{ + union smb_lock parms; + struct smb_lock_entry lock[1]; + NTSTATUS status; + + if (!(cli->transport->negotiate.capabilities & CAP_LARGE_FILES)) { + return cli_unlock(cli, fnum, offset, len); + } + + parms.lockx.level = RAW_LOCK_LOCKX; + parms.lockx.in.fnum = fnum; + parms.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; + parms.lockx.in.timeout = 0; + parms.lockx.in.ulock_cnt = 1; + parms.lockx.in.lock_cnt = 0; + lock[0].pid = cli->session->pid; + lock[0].offset = offset; + lock[0].count = len; + parms.lockx.in.locks = &lock[0]; + + status = smb_raw_lock(cli->tree, &parms); + + return NT_STATUS_IS_OK(status); +} + + +/**************************************************************************** + Do a SMBgetattrE call. +****************************************************************************/ +BOOL cli_getattrE(struct cli_state *cli, int fd, + uint16 *attr, size_t *size, + time_t *c_time, time_t *a_time, time_t *m_time) +{ + union smb_fileinfo parms; + NTSTATUS status; + + parms.getattre.level = RAW_FILEINFO_GETATTRE; + parms.getattre.in.fnum = fd; + + status = smb_raw_fileinfo(cli->tree, NULL, &parms); + + if (!NT_STATUS_IS_OK(status)) + return False; + + if (size) { + *size = parms.getattre.out.size; + } + + if (attr) { + *attr = parms.getattre.out.attrib; + } + + if (c_time) { + *c_time = parms.getattre.out.create_time; + } + + if (a_time) { + *a_time = &parms.getattre.out.access_time; + } + + if (m_time) { + *m_time = &parms.getattre.out.write_time; + } + + return True; +} + +/**************************************************************************** + Do a SMBgetatr call +****************************************************************************/ +BOOL cli_getatr(struct cli_state *cli, const char *fname, + uint16 *attr, size_t *size, time_t *t) +{ + union smb_fileinfo parms; + NTSTATUS status; + + parms.getattr.level = RAW_FILEINFO_GETATTR; + parms.getattr.in.fname = fname; + + status = smb_raw_pathinfo(cli->tree, NULL, &parms); + + if (!NT_STATUS_IS_OK(status)) { + return False; + } + + if (size) { + *size = parms.getattr.out.size; + } + + if (t) { + *t = parms.getattr.out.write_time; + } + + if (attr) { + *attr = parms.getattr.out.attrib; + } + + return True; +} + + +/**************************************************************************** + Do a SMBsetatr call. +****************************************************************************/ +BOOL cli_setatr(struct cli_state *cli, const char *fname, uint16 mode, time_t t) +{ + union smb_setfileinfo parms; + NTSTATUS status; + + parms.setattr.level = RAW_SFILEINFO_SETATTR; + parms.setattr.in.attrib = mode; + parms.setattr.in.write_time = t; + parms.setattr.file.fname = fname; + + status = smb_raw_setpathinfo(cli->tree, &parms); + + return NT_STATUS_IS_OK(status); +} + + +/**************************************************************************** + Check for existence of a dir. +****************************************************************************/ +BOOL cli_chkpath(struct cli_state *cli, const char *path) +{ + struct smb_chkpath parms; + char *path2; + NTSTATUS status; + + path2 = strdup(path); + trim_string(path2,NULL,"\\"); + if (!*path2) { + free(path2); + path2 = strdup("\\"); + } + + parms.in.path = path2; + + status = smb_raw_chkpath(cli->tree, &parms); + + free(path2); + + return NT_STATUS_IS_OK(status); +} + + +/**************************************************************************** + Query disk space. +****************************************************************************/ +BOOL cli_dskattr(struct cli_state *cli, int *bsize, int *total, int *avail) +{ + union smb_fsinfo fsinfo_parms; + TALLOC_CTX *mem_ctx; + NTSTATUS status; + + mem_ctx = talloc_init("cli_dskattr"); + + fsinfo_parms.dskattr.level = RAW_QFS_DSKATTR; + status = smb_raw_fsinfo(cli->tree, mem_ctx, &fsinfo_parms); + if (NT_STATUS_IS_OK(status)) { + *bsize = fsinfo_parms.dskattr.out.block_size; + *total = fsinfo_parms.dskattr.out.units_total; + *avail = fsinfo_parms.dskattr.out.units_free; + } + + talloc_destroy(mem_ctx); + + return NT_STATUS_IS_OK(status); +} + + +/**************************************************************************** + Create and open a temporary file. +****************************************************************************/ +int cli_ctemp(struct cli_state *cli, const char *path, char **tmp_path) +{ + union smb_open open_parms; + TALLOC_CTX *mem_ctx; + NTSTATUS status; + + mem_ctx = talloc_init("raw_open"); + if (!mem_ctx) return -1; + + open_parms.openx.level = RAW_OPEN_CTEMP; + open_parms.ctemp.in.attrib = 0; + open_parms.ctemp.in.directory = path; + + status = smb_raw_open(cli->tree, mem_ctx, &open_parms); + if (tmp_path) { + *tmp_path = strdup(open_parms.ctemp.out.name); + } + talloc_destroy(mem_ctx); + if (NT_STATUS_IS_OK(status)) { + return open_parms.ctemp.out.fnum; + } + return -1; +} + diff --git a/source4/libcli/clilist.c b/source4/libcli/clilist.c new file mode 100644 index 0000000000..620e4382f4 --- /dev/null +++ b/source4/libcli/clilist.c @@ -0,0 +1,313 @@ +/* + Unix SMB/CIFS implementation. + client directory list routines + Copyright (C) Andrew Tridgell 1994-2003 + Copyright (C) James Myers 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 search_private { + file_info *dirlist; + TALLOC_CTX *mem_ctx; + int dirlist_len; + int ff_searchcount; /* total received in 1 server trip */ + int total_received; /* total received all together */ + int info_level; + DATA_BLOB status; /* used for old-style search */ +}; + + +/**************************************************************************** + Interpret a long filename structure. +****************************************************************************/ +static BOOL interpret_long_filename(int level, + union smb_search_data *info, + file_info *finfo) +{ + file_info finfo2; + + if (!finfo) finfo = &finfo2; + ZERO_STRUCTP(finfo); + + finfo->size = info->both_directory_info.size; + finfo->ctime = nt_time_to_unix(&info->both_directory_info.create_time); + finfo->atime = nt_time_to_unix(&info->both_directory_info.access_time); + finfo->mtime = nt_time_to_unix(&info->both_directory_info.write_time); + finfo->mode = info->both_directory_info.attrib; /* 32 bit->16 bit attrib */ + if (info->both_directory_info.short_name.s) { + strncpy(finfo->short_name, info->both_directory_info.short_name.s, + sizeof(finfo->short_name)-1); + } + finfo->name = info->both_directory_info.name.s; + return True; +} + +/* callback function used for trans2 search */ +static BOOL cli_list_new_callback(void *private, union smb_search_data *file) +{ + struct search_private *state = (struct search_private*) private; + file_info *tdl; + + /* add file info to the dirlist pool */ + tdl = talloc_realloc(state->mem_ctx, state->dirlist, + state->dirlist_len + sizeof(struct file_info)); + + if (!tdl) { + return False; + } + state->dirlist = tdl; + state->dirlist_len += sizeof(struct file_info); + + interpret_long_filename(state->info_level, file, &state->dirlist[state->total_received]); + + state->total_received++; + state->ff_searchcount++; + + return True; +} + +int cli_list_new(struct cli_state *cli, const char *Mask, uint16 attribute, + void (*fn)(file_info *, const char *, void *), void *caller_state) +{ + union smb_search_first first_parms; + union smb_search_next next_parms; + struct search_private state; /* for callbacks */ + int received = 0; + BOOL first = True; + int num_received = 0; + int max_matches = 512; + char *mask; + int ff_eos = 0, i, ff_searchcount; + int ff_dir_handle=0; + int level; + + /* initialize state for search */ + state.dirlist = NULL; + state.mem_ctx = talloc_init("cli_list_new"); + state.dirlist_len = 0; + state.total_received = 0; + + mask = talloc_strdup(state.mem_ctx, Mask); + + if (cli->transport->negotiate.capabilities & CAP_NT_SMBS) { + level = RAW_SEARCH_BOTH_DIRECTORY_INFO; + } else { + level = RAW_SEARCH_STANDARD; + } + + while (1) { + state.ff_searchcount = 0; + if (first) { + NTSTATUS status; + + first_parms.t2ffirst.level = level; + first_parms.t2ffirst.in.max_count = max_matches; + first_parms.t2ffirst.in.search_attrib = attribute; + first_parms.t2ffirst.in.pattern = mask; + first_parms.t2ffirst.in.flags = FLAG_TRANS2_FIND_CLOSE_IF_END; + first_parms.t2ffirst.in.storage_type = 0; + + status = smb_raw_search_first(cli->tree, + state.mem_ctx, &first_parms, + (void*)&state, cli_list_new_callback); + if (!NT_STATUS_IS_OK(status)) { + talloc_destroy(state.mem_ctx); + return -1; + } + + ff_dir_handle = first_parms.t2ffirst.out.handle; + ff_searchcount = first_parms.t2ffirst.out.count; + ff_eos = first_parms.t2ffirst.out.end_of_search; + + received = first_parms.t2ffirst.out.count; + if (received <= 0) break; + if (ff_eos) break; + first = False; + } else { + NTSTATUS status; + + next_parms.t2fnext.level = level; + next_parms.t2fnext.in.max_count = max_matches; + next_parms.t2fnext.in.last_name = mask; + next_parms.t2fnext.in.handle = ff_dir_handle; + next_parms.t2fnext.in.resume_key = 0; + next_parms.t2fnext.in.flags = FLAG_TRANS2_FIND_CONTINUE | FLAG_TRANS2_FIND_CLOSE_IF_END; + + status = smb_raw_search_next(cli->tree, + state.mem_ctx, + &next_parms, + (void*)&state, + cli_list_new_callback); + + if (!NT_STATUS_IS_OK(status)) { + return -1; + } + ff_searchcount = next_parms.t2fnext.out.count; + ff_eos = next_parms.t2fnext.out.end_of_search; + received = next_parms.t2fnext.out.count; + if (received <= 0) break; + if (ff_eos) break; + } + + num_received += received; + } + + for (i=0;ictime = info->search.write_time; + finfo->atime = info->search.write_time; + finfo->mtime = info->search.write_time; + finfo->size = info->search.size; + finfo->mode = info->search.attrib; + finfo->name = info->search.name; + return True; +} + +/* callback function used for smb_search */ +static BOOL cli_list_old_callback(void *private, union smb_search_data *file) +{ + struct search_private *state = (struct search_private*) private; + file_info *tdl; + + /* add file info to the dirlist pool */ + tdl = talloc_realloc(state->mem_ctx, state->dirlist, + state->dirlist_len + sizeof(struct file_info)); + + if (!tdl) { + return False; + } + state->dirlist = tdl; + state->dirlist_len += sizeof(struct file_info); + + interpret_short_filename(state->info_level, file, &state->dirlist[state->total_received]); + + state->total_received++; + state->ff_searchcount++; + state->status = file->search.search_id; /* return resume info */ + + return True; +} + +int cli_list_old(struct cli_state *cli, const char *Mask, uint16 attribute, + void (*fn)(file_info *, const char *, void *), void *caller_state) +{ + union smb_search_first first_parms; + union smb_search_next next_parms; + struct search_private state; /* for callbacks */ + const int num_asked = 500; + int received = 0; + BOOL first = True; + int num_received = 0; + char *mask; + int i; + + /* initialize state for search */ + state.dirlist = NULL; + state.mem_ctx = talloc_init("cli_list_old"); + state.dirlist_len = 0; + state.total_received = 0; + + mask = talloc_strdup(state.mem_ctx, Mask); + + while (1) { + state.ff_searchcount = 0; + if (first) { + NTSTATUS status; + + first_parms.search_first.level = RAW_SEARCH_SEARCH; + first_parms.search_first.in.max_count = num_asked; + first_parms.search_first.in.search_attrib = attribute; + first_parms.search_first.in.pattern = mask; + + status = smb_raw_search_first(cli->tree, state.mem_ctx, + &first_parms, + (void*)&state, + cli_list_old_callback); + if (!NT_STATUS_IS_OK(status)) { + talloc_destroy(state.mem_ctx); + return -1; + } + + received = first_parms.search_first.out.count; + if (received <= 0) break; + first = False; + } else { + NTSTATUS status; + + next_parms.search_next.level = RAW_SEARCH_SEARCH; + next_parms.search_next.in.max_count = num_asked; + next_parms.search_next.in.search_attrib = attribute; + next_parms.search_next.in.search_id = state.status; + + status = smb_raw_search_next(cli->tree, state.mem_ctx, + &next_parms, + (void*)&state, + cli_list_old_callback); + + if (!NT_STATUS_IS_OK(status)) { + talloc_destroy(state.mem_ctx); + return -1; + } + received = next_parms.search_next.out.count; + if (received <= 0) break; + } + + num_received += received; + } + + for (i=0;itransport->negotiate.protocol <= PROTOCOL_LANMAN1) + return cli_list_old(cli, Mask, attribute, fn, state); + return cli_list_new(cli, Mask, attribute, fn, state); +} diff --git a/source4/libcli/climessage.c b/source4/libcli/climessage.c new file mode 100644 index 0000000000..ad5d41545b --- /dev/null +++ b/source4/libcli/climessage.c @@ -0,0 +1,93 @@ +/* + Unix SMB/CIFS implementation. + client message handling routines + Copyright (C) Andrew Tridgell 1994-1998 + Copyright (C) James J Myers 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + + +/**************************************************************************** +start a message sequence +****************************************************************************/ +BOOL cli_message_start(struct cli_state *cli, char *host, char *username, + int *grp) +{ + struct cli_request *req; + + req = cli_request_setup(cli->tree, SMBsendstrt, 0, 0); + cli_req_append_string(req, username, STR_TERMINATE); + cli_req_append_string(req, host, STR_TERMINATE); + if (!cli_request_send(req) || + !cli_request_receive(req) || + cli_is_error(cli)) { + cli_request_destroy(req); + return False; + } + + *grp = SVAL(req->in.vwv, VWV(0)); + cli_request_destroy(req); + + return True; +} + + +/**************************************************************************** +send a message +****************************************************************************/ +BOOL cli_message_text(struct cli_state *cli, char *msg, int len, int grp) +{ + struct cli_request *req; + + req = cli_request_setup(cli->tree, SMBsendtxt, 1, 0); + SSVAL(req->out.vwv, VWV(0), grp); + + cli_req_append_bytes(req, msg, len); + + if (!cli_request_send(req) || + !cli_request_receive(req) || + cli_is_error(cli)) { + cli_request_destroy(req); + return False; + } + + cli_request_destroy(req); + return True; +} + +/**************************************************************************** +end a message +****************************************************************************/ +BOOL cli_message_end(struct cli_state *cli, int grp) +{ + struct cli_request *req; + + req = cli_request_setup(cli->tree, SMBsendend, 1, 0); + SSVAL(req->out.vwv, VWV(0), grp); + + if (!cli_request_send(req) || + !cli_request_receive(req) || + cli_is_error(cli)) { + cli_request_destroy(req); + return False; + } + + cli_request_destroy(req); + return True; +} + diff --git a/source4/libcli/clireadwrite.c b/source4/libcli/clireadwrite.c new file mode 100644 index 0000000000..e1d154b283 --- /dev/null +++ b/source4/libcli/clireadwrite.c @@ -0,0 +1,155 @@ +/* + Unix SMB/CIFS implementation. + client file read/write routines + Copyright (C) Andrew Tridgell 1994-1998 + Copyright (C) James Myers 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +/**************************************************************************** + 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) +{ + union smb_read parms; + int readsize; + ssize_t total = 0; + + if (size == 0) { + return 0; + } + + parms.readx.level = RAW_READ_READX; + parms.readx.in.fnum = fnum; + + /* + * Set readsize to the maximum size we can handle in one readX, + * rounded down to a multiple of 1024. + */ + readsize = (cli->transport->negotiate.max_xmit - (MIN_SMB_SIZE+32)) & ~1023; + + while (total < size) { + NTSTATUS status; + + readsize = MIN(readsize, size-total); + + parms.readx.in.offset = offset; + parms.readx.in.mincnt = readsize; + parms.readx.in.maxcnt = readsize; + parms.readx.in.remaining = size - total; + parms.readx.out.data = buf + total; + + status = smb_raw_read(cli->tree, &parms); + + if (!NT_STATUS_IS_OK(status)) { + return -1; + } + + total += parms.readx.out.nread; + offset += parms.readx.out.nread; + + /* If the server returned less than we asked for we're at EOF */ + if (parms.readx.out.nread < readsize) + break; + } + + return total; +} + + +/**************************************************************************** + write to a file + write_mode: 0x0001 disallow write cacheing + 0x0002 return bytes remaining + 0x0004 use raw named pipe protocol + 0x0008 start of message mode named pipe protocol +****************************************************************************/ +ssize_t cli_write(struct cli_state *cli, + int fnum, uint16 write_mode, + const char *buf, off_t offset, size_t size) +{ + union smb_write parms; + int block = (cli->transport->negotiate.max_xmit - (MIN_SMB_SIZE+32)) & ~1023; + ssize_t total = 0; + + if (size == 0) { + return 0; + } + + parms.writex.level = RAW_WRITE_WRITEX; + parms.writex.in.fnum = fnum; + parms.writex.in.wmode = write_mode; + parms.writex.in.remaining = 0; + + while (total < size) { + NTSTATUS status; + + block = MIN(block, size - total); + + parms.writex.in.offset = offset; + parms.writex.in.count = block; + parms.writex.in.data = buf; + + status = smb_raw_write(cli->tree, &parms); + + if (!NT_STATUS_IS_OK(status)) { + return -1; + } + + offset += parms.writex.out.nwritten; + total += parms.writex.out.nwritten; + buf += parms.writex.out.nwritten; + } + + return total; +} + +/**************************************************************************** + write to a file using a SMBwrite and not bypassing 0 byte writes +****************************************************************************/ +ssize_t cli_smbwrite(struct cli_state *cli, + int fnum, char *buf, off_t offset, size_t size1) +{ + union smb_write parms; + ssize_t total = 0; + + parms.write.level = RAW_WRITE_WRITE; + parms.write.in.remaining = 0; + + do { + size_t size = MIN(size1, cli->transport->negotiate.max_xmit - 48); + + parms.write.in.fnum = fnum; + parms.write.in.offset = offset; + parms.write.in.count = size; + parms.write.in.data = buf + total; + + if (NT_STATUS_IS_ERR(smb_raw_write(cli->tree, &parms))) + return -1; + + size = parms.write.out.nwritten; + if (size == 0) + break; + + size1 -= size; + total += size; + offset += size; + } while (size1); + + return total; +} diff --git a/source4/libcli/clisecdesc.c b/source4/libcli/clisecdesc.c new file mode 100644 index 0000000000..66272251ec --- /dev/null +++ b/source4/libcli/clisecdesc.c @@ -0,0 +1,121 @@ +/* + Unix SMB/CIFS implementation. + client security descriptor functions + Copyright (C) Andrew Tridgell 2000 + Copyright (C) James J Myers 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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) +{ + struct smb_nttrans parms; + char param[8]; + DATA_BLOB param_blob; + prs_struct pd; + SEC_DESC *psd = NULL; + NTSTATUS status; + + param_blob.length = 8; + param_blob.data = ¶m[0]; + + SIVAL(param, 0, fnum); + SSVAL(param, 4, 0x7); + + parms.in.max_param = 1024; + parms.in.max_data = 1024; + parms.in.max_setup = 0; + parms.in.setup_count = 18; + parms.in.function = NT_TRANSACT_QUERY_SECURITY_DESC; + parms.in.params = param_blob; + parms.in.data = data_blob(NULL, 0); + + status = smb_raw_nttrans(cli->tree, mem_ctx, &parms); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1,("Failed to send NT_TRANSACT_QUERY_SECURITY_DESC\n")); + goto cleanup; + } + + prs_init(&pd, parms.out.data.length, mem_ctx, UNMARSHALL); + prs_copy_data_in(&pd, parms.out.data.data, parms.out.data.length); + prs_set_offset(&pd,0); + + if (!sec_io_desc("sd data", &psd, &pd, 1)) { + DEBUG(1,("Failed to parse secdesc\n")); + goto cleanup; + } + + cleanup: + 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) +{ + struct smb_nttrans parms; + char param[8]; + DATA_BLOB param_blob; + prs_struct pd; + BOOL ret = False; + TALLOC_CTX *mem_ctx; + NTSTATUS status; + + mem_ctx = talloc_init("cli_set_secdesc"); + + 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; + } + + param_blob.length = 8; + param_blob.data = ¶m[0]; + + SIVAL(param, 0, fnum); + SSVAL(param, 4, 0x7); + + parms.in.max_param = 1000; + parms.in.max_data = 1000; + parms.in.max_setup = 0; + parms.in.setup_count = 18; + parms.in.function = NT_TRANSACT_SET_SECURITY_DESC; + parms.in.params = param_blob; + parms.in.data = data_blob(NULL, 0); + + status = smb_raw_nttrans(cli->tree, mem_ctx, &parms); + + if (NT_STATUS_IS_ERR(status)) { + DEBUG(1,("Failed to send NT_TRANSACT_SET_SECURITY_DESC\n")); + goto cleanup; + } + ret = True; + + cleanup: + prs_mem_free(&pd); + talloc_destroy(mem_ctx); + return ret; +} diff --git a/source4/libcli/clitrans2.c b/source4/libcli/clitrans2.c new file mode 100644 index 0000000000..6fd5c2ce28 --- /dev/null +++ b/source4/libcli/clitrans2.c @@ -0,0 +1,221 @@ +/* + Unix SMB/CIFS implementation. + client trans2 calls + Copyright (C) James J Myers 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +/**************************************************************************** +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) +{ + union smb_fileinfo parms; + TALLOC_CTX *mem_ctx; + NTSTATUS status; + + mem_ctx = talloc_init("cli_qpathinfo"); + if (!mem_ctx) return False; + + parms.standard.level = RAW_FILEINFO_STANDARD; + parms.standard.in.fname = fname; + + status = smb_raw_pathinfo(cli->tree, mem_ctx, &parms); + talloc_destroy(mem_ctx); + if (!NT_STATUS_IS_OK(status)) { + return False; + } + + if (c_time) { + *c_time = parms.standard.out.create_time; + } + if (a_time) { + *a_time = parms.standard.out.access_time; + } + if (m_time) { + *m_time = parms.standard.out.write_time; + } + if (size) { + *size = parms.standard.out.size; + } + if (mode) { + *mode = parms.standard.out.attrib; + } + + return 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) +{ + union smb_fileinfo parms; + TALLOC_CTX *mem_ctx; + NTSTATUS status; + + mem_ctx = talloc_init("cli_qfilename"); + if (!mem_ctx) return False; + + parms.all_info.level = RAW_FILEINFO_ALL_INFO; + parms.all_info.in.fname = fname; + + status = smb_raw_pathinfo(cli->tree, mem_ctx, &parms); + talloc_destroy(mem_ctx); + if (!NT_STATUS_IS_OK(status)) { + return False; + } + + if (c_time) { + *c_time = nt_time_to_unix(&parms.all_info.out.create_time); + } + if (a_time) { + *a_time = nt_time_to_unix(&parms.all_info.out.access_time); + } + if (m_time) { + *m_time = nt_time_to_unix(&parms.all_info.out.change_time); + } + if (w_time) { + *w_time = nt_time_to_unix(&parms.all_info.out.write_time); + } + if (size) { + *size = parms.all_info.out.size; + } + if (mode) { + *mode = parms.all_info.out.attrib; + } + + return True; +} + + +/**************************************************************************** +send a qfileinfo QUERY_FILE_NAME_INFO call +****************************************************************************/ +BOOL cli_qfilename(struct cli_state *cli, int fnum, + const char **name) +{ + union smb_fileinfo parms; + TALLOC_CTX *mem_ctx; + NTSTATUS status; + + mem_ctx = talloc_init("cli_qfilename"); + if (!mem_ctx) return False; + + parms.name_info.level = RAW_FILEINFO_NAME_INFO; + parms.name_info.in.fnum = fnum; + + status = smb_raw_fileinfo(cli->tree, mem_ctx, &parms); + if (!NT_STATUS_IS_OK(status)) { + talloc_destroy(mem_ctx); + *name = NULL; + return False; + } + + *name = strdup(parms.name_info.out.fname.s); + + talloc_destroy(mem_ctx); + + 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) +{ + union smb_fileinfo parms; + TALLOC_CTX *mem_ctx; + NTSTATUS status; + + mem_ctx = talloc_init("cli_qfileinfo"); + if (!mem_ctx) return False; + + parms.all_info.level = RAW_FILEINFO_ALL_INFO; + parms.all_info.in.fnum = fnum; + + status = smb_raw_fileinfo(cli->tree, mem_ctx, &parms); + talloc_destroy(mem_ctx); + if (!NT_STATUS_IS_OK(status)) { + return False; + } + + if (c_time) { + *c_time = nt_time_to_unix(&parms.all_info.out.create_time); + } + if (a_time) { + *a_time = nt_time_to_unix(&parms.all_info.out.access_time); + } + if (m_time) { + *m_time = nt_time_to_unix(&parms.all_info.out.change_time); + } + if (w_time) { + *w_time = nt_time_to_unix(&parms.all_info.out.write_time); + } + if (mode) { + *mode = parms.all_info.out.attrib; + } + if (size) { + *size = (size_t)parms.all_info.out.size; + } + if (ino) { + *ino = 0; + } + + return True; +} + + +/**************************************************************************** +send a qpathinfo SMB_QUERY_FILE_ALT_NAME_INFO call +****************************************************************************/ +NTSTATUS cli_qpathinfo_alt_name(struct cli_state *cli, const char *fname, + const char **alt_name) +{ + union smb_fileinfo parms; + TALLOC_CTX *mem_ctx; + NTSTATUS status; + + parms.alt_name_info.level = RAW_FILEINFO_ALT_NAME_INFO; + parms.alt_name_info.in.fname = fname; + + mem_ctx = talloc_init("cli_qpathinfo_alt_name"); + if (!mem_ctx) return NT_STATUS_NO_MEMORY; + + status = smb_raw_pathinfo(cli->tree, mem_ctx, &parms); + if (!NT_STATUS_IS_OK(status)) { + talloc_destroy(mem_ctx); + *alt_name = NULL; + return cli_nt_error(cli); + } + + *alt_name = strdup(parms.alt_name_info.out.fname.s); + + talloc_destroy(mem_ctx); + + return NT_STATUS_OK; +} diff --git a/source4/libcli/namecache.c b/source4/libcli/namecache.c new file mode 100644 index 0000000000..9f4796af1a --- /dev/null +++ b/source4/libcli/namecache.c @@ -0,0 +1,246 @@ +/* + 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_enableTODO(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_shutdownTODO(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(TALLOC_CTX *mem_ctx, const char *name, int name_type) +{ + char *keystr; + asprintf(&keystr, NBTKEY_FMT, strupper_talloc(mem_ctx, 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(TALLOC_CTX *mem_ctx, const char *name, int name_type, + int num_names, struct in_addr *ip_list) +{ + time_t expiry; + char *key, *value_string; + int i; + + /* + * we use gecache call to avoid annoying debug messages about + * initialised namecache again and again... + */ + if (!gencache_init()) return False; + + 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%s", inet_ntoa(ip_list[i]), + i == (num_names - 1) ? "" : ", ")); + + DEBUGADD(5, ("\n")); + + key = namecache_key(mem_ctx, name, name_type); + + /* + * Cache pdc location or dc lists for only a little while + * otherwise if we lock on to a bad DC we can potentially be + * out of action for the entire cache timeout time! + */ + + if (name_type == 0x1b || name_type == 0x1c) + expiry = time(NULL) + 10; + else + 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 + */ + ipstr_list_make(&value_string, ip_list, num_names); + + /* set the entry */ + return (gencache_set(key, value_string, expiry)); +} + + +/** + * 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(TALLOC_CTX *mem_ctx, const char *name, int name_type, struct in_addr **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(mem_ctx, name, name_type); + + if (!gencache_get(key, &value, &timeout)) { + DEBUG(5, ("no entry for %s#%02X found.\n", name, name_type)); + SAFE_FREE(key); + 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")); +} + diff --git a/source4/libcli/namequery.c b/source4/libcli/namequery.c new file mode 100644 index 0000000000..2f6343dcaf --- /dev/null +++ b/source4/libcli/namequery.c @@ -0,0 +1,1333 @@ +/* + 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(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_string(ret[i].name,NULL," "); + 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, char *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))); + + 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;is_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; +} + +/* + 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); +} + + +/**************************************************************************** + 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 */ + DEBUG(3,("Negative name query response, rcode 0x%02x: ", nmb2->header.rcode )); + switch( nmb2->header.rcode ) { + case 0x01: + DEBUG(3,("Request was invalidly formatted.\n" )); + break; + case 0x02: + DEBUG(3,("Problem with NBNS, cannot process name.\n")); + break; + case 0x03: + DEBUG(3,("The name requested does not exist.\n" )); + break; + case 0x04: + DEBUG(3,("Unsupported request error.\n" )); + break; + case 0x05: + DEBUG(3,("Query refused error.\n" )); + break; + default: + DEBUG(3,("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;ianswers->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; + } + } + + if (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( TALLOC_CTX *mem_ctx, + 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(mem_ctx, 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); +} + + +/******************************************************** + Resolve via "bcast" method. +*********************************************************/ + +BOOL name_resolve_bcast(const char *name, int name_type, + struct in_addr **return_ip_list, int *return_count) +{ + int sock, i; + int num_interfaces = iface_count(); + + if (lp_disable_netbios()) { + DEBUG(5,("name_resolve_bcast(%s#%02x): netbios is disabled\n", name, name_type)); + return False; + } + + *return_ip_list = 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); + *return_ip_list = name_query(sock, name, name_type, True, + True, sendto_ip, return_count, &flags, NULL); + if(*return_ip_list != NULL) { + close(sock); + return True; + } + } + + close(sock); + return False; +} + +/******************************************************** + Resolve via "wins" method. +*********************************************************/ +BOOL resolve_wins(TALLOC_CTX *mem_ctx, const char *name, int name_type, + struct in_addr **return_iplist, int *return_count) +{ + int sock, t, i; + char **wins_tags; + struct in_addr src_ip; + + 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(mem_ctx, 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\n", name)); + + 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 in_addr *)malloc(sizeof(struct in_addr)); + if(*return_iplist == NULL) { + DEBUG(3,("resolve_hosts: malloc fail !\n")); + return False; + } + **return_iplist = return_ip; + *return_count = 1; + return True; + } + 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. +*********************************************************/ + +static BOOL internal_resolve_name(TALLOC_CTX *mem_ctx, const char *name, int name_type, + struct in_addr **return_iplist, int *return_count) +{ + char *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; + struct in_addr *nodupes_iplist; + 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) { + *return_iplist = (struct in_addr *)malloc(sizeof(struct in_addr)); + if(*return_iplist == NULL) { + DEBUG(3,("internal_resolve_name: malloc fail !\n")); + return False; + } + if(is_address) { + /* if it's in the form of an IP address then get the lib to interpret it */ + if (((*return_iplist)->s_addr = inet_addr(name)) == 0xFFFFFFFF ){ + DEBUG(1,("internal_resolve_name: inet_addr failed on %s\n", name)); + return False; + } + } else { + (*return_iplist)->s_addr = allones ? 0xFFFFFFFF : 0; + *return_count = 1; + } + return True; + } + + /* Check netbios name cache */ + + if (namecache_fetch(mem_ctx, name, name_type, return_iplist, return_count)) { + + /* This could be a negative response */ + + return (*return_count > 0); + } + + name_resolve_list = talloc_strdup(mem_ctx, lp_name_resolve_order()); + ptr = name_resolve_list; + if (!ptr || !*ptr) + ptr = "host"; + + while (next_token(&ptr, tok, LIST_SEP, sizeof(tok))) { + if((strequal(tok, "host") || strequal(tok, "hosts"))) { + if (name_type == 0x20) { + if (resolve_hosts(name, return_iplist, return_count)) { + result = True; + goto done; + } + } + } else if(strequal( tok, "lmhosts")) { + /* REWRITE: add back in? */ + DEBUG(0,("resolve_name: REWRITE: add lmhosts back?? %s\n", tok)); + } else if(strequal( tok, "wins")) { + /* don't resolve 1D via WINS */ + if (name_type != 0x1D && + resolve_wins(mem_ctx, 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 && (nodupes_iplist = (struct in_addr *) + malloc(sizeof(struct in_addr) * (*return_count)))) { + int nodupes_count = 0; + + /* Iterate over return_iplist looking for duplicates */ + + for (i = 0; i < *return_count; i++) { + BOOL is_dupe = False; + int j; + + for (j = i + 1; j < *return_count; j++) { + if (ip_equal((*return_iplist)[i], + (*return_iplist)[j])) { + is_dupe = True; + break; + } + } + + if (!is_dupe) { + + /* This one not a duplicate */ + + nodupes_iplist[nodupes_count] = (*return_iplist)[i]; + nodupes_count++; + } + } + + /* Switcheroo with original list */ + + free(*return_iplist); + + *return_iplist = nodupes_iplist; + *return_count = nodupes_count; + } + + /* Save in name cache */ + for (i = 0; i < *return_count && DEBUGLEVEL == 100; i++) + DEBUG(100, ("Storing name %s of type %d (ip: %s)\n", name, + name_type, inet_ntoa((*return_iplist)[i]))); + + namecache_store(mem_ctx, name, name_type, *return_count, *return_iplist); + + /* Display some debugging info */ + + DEBUG(10, ("internal_resolve_name: returning %d addresses: ", + *return_count)); + + for (i = 0; i < *return_count; i++) + DEBUGADD(10, ("%s ", inet_ntoa((*return_iplist)[i]))); + + 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(TALLOC_CTX *mem_ctx, const char *name, struct in_addr *return_ip, int name_type) +{ + struct in_addr *ip_list = NULL; + int count = 0; + + if (is_ipaddress(name)) { + *return_ip = *interpret_addr2(mem_ctx, name); + return True; + } + + if (internal_resolve_name(mem_ctx, name, name_type, &ip_list, &count)) { + int i; + /* only return valid addresses for TCP connections */ + for (i=0; iheader.msg_type = 0x10; + dgram->header.flags.node_type = M_NODE; + dgram->header.flags.first = True; + dgram->header.flags.more = False; + dgram->header.dgm_id = dgm_id; + dgram->header.source_ip = *iface_ip(*pdc_ip); + dgram->header.source_port = ntohs(sock_name.sin_port); + dgram->header.dgm_length = 0; /* Let build_dgram() handle this. */ + dgram->header.packet_offset = 0; + + make_nmb_name(&dgram->source_name,srcname,0); + make_nmb_name(&dgram->dest_name,domain,0x1C); + + 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); + + CVAL(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); + pstrcpy(p2,mailslot); + p2 = skip_string(p2,1); + + memcpy(p2,buffer,len); + p2 += len; + + dgram->datasize = PTR_DIFF(p2,ptr+4); /* +4 for tcp length. */ + + p.ip = *pdc_ip; + p.port = DGRAM_PORT; + p.fd = sock; + p.timestamp = time(NULL); + p.packet_type = DGRAM_PACKET; + + GetTimeOfDay(&tval); + + if (!send_packet(&p)) { + DEBUG(0,("lookup_pdc_name: send_packet failed.\n")); + close(sock); + return False; + } + + retries--; + + while (1) { + struct timeval tval2; + struct packet_struct *p_ret; + + GetTimeOfDay(&tval2); + if (TvalDiff(&tval,&tval2) > retry_time) { + if (!retries) + break; + if (!send_packet(&p)) { + DEBUG(0,("lookup_pdc_name: send_packet failed.\n")); + close(sock); + return False; + } + GetTimeOfDay(&tval); + retries--; + } + + if ((p_ret = receive_dgram_packet(sock,90,mailslot_name))) { + struct dgram_packet *dgram2 = &p_ret->packet.dgram; + char *buf; + char *buf2; + + buf = &dgram2->data[0]; + buf -= 4; + + if (CVAL(buf,smb_com) != SMBtrans) { + DEBUG(0,("lookup_pdc_name: datagram type %u != SMBtrans(%u)\n", (unsigned int) + CVAL(buf,smb_com), (unsigned int)SMBtrans )); + free_packet(p_ret); + continue; + } + + len = SVAL(buf,smb_vwv11); + buf2 = smb_base(buf) + SVAL(buf,smb_vwv12); + + if (len <= 0) { + DEBUG(0,("lookup_pdc_name: datagram len < 0 (%d)\n", len )); + free_packet(p_ret); + continue; + } + + DEBUG(4,("lookup_pdc_name: datagram reply from %s to %s IP %s for %s of type %d len=%d\n", + nmb_namestr(&dgram2->source_name),nmb_namestr(&dgram2->dest_name), + inet_ntoa(p_ret->ip), smb_buf(buf),SVAL(buf2,0),len)); + + if(SVAL(buf2,0) != QUERYFORPDC_R) { + DEBUG(0,("lookup_pdc_name: datagram type (%u) != QUERYFORPDC_R(%u)\n", + (unsigned int)SVAL(buf,0), (unsigned int)QUERYFORPDC_R )); + free_packet(p_ret); + continue; + } + + buf2 += 2; + /* Note this is safe as it is a bounded strcpy. */ + fstrcpy(ret_name, buf2); + ret_name[sizeof(fstring)-1] = '\0'; + close(sock); + free_packet(p_ret); + return True; + } + } + + close(sock); + return False; +#endif /* defined(I_HATE_WINDOWS_REPLY_CODE) */ +} + +/******************************************************** + Get the IP address list of the primary domain controller + for a domain. +*********************************************************/ + +BOOL get_pdc_ip(TALLOC_CTX *mem_ctx, const char *domain, struct in_addr *ip) +{ + struct in_addr *ip_list; + int count; + int i = 0; + + /* Look up #1B name */ + + if (!internal_resolve_name(mem_ctx, domain, 0x1b, &ip_list, &count)) + 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)); + + /* look for a local net */ + for ( i=0; i 1) ) { + qsort(ip_list, count, sizeof(struct in_addr), QSORT_CAST ip_compare); + } + + for (i = 0; i < count; i++) { + if (is_zero_ip(ip_list[i])) + continue; + + if (name_status_find(domain, 0x1c, 0x20, ip_list[i], srv_name)) { + dc_ip = ip_list[i]; + goto done; + } + } + + + SAFE_FREE(ip_list); + + return False; +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_find_dc: 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; +} + diff --git a/source4/libcli/nmblib.c b/source4/libcli/nmblib.c new file mode 100644 index 0000000000..a875f4652e --- /dev/null +++ b/source4/libcli/nmblib.c @@ -0,0 +1,1287 @@ +/* + 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" + +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 ""; +} + +/**************************************************************************** + 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+= 16) + { + DEBUGADD(4, (" %s %3x char ", hdr, i)); + + for (j = 0; j < 16; j++) + { + uchar 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 < 16; j++) + { + if (i+j >= res->rdlength) break; + DEBUGADD(4, ("%02X", (uchar)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)) { + DEBUG(4, ("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))); + DEBUG(4, (" 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))); + DEBUG(4, (" 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(uchar *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; + uchar *ubuf = (uchar *)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) { + uchar 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==16) { + /* parse out the name type, + its always in the 16th byte of the name */ + name->name_type = ((uchar)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 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; + fstring buf1; + char *p; + + if (strcmp(name->name,"*") == 0) { + /* special case for wildcard name */ + memset(buf1,'\0',20); + buf1[0] = '*'; + buf1[15] = name->name_type; + } else { + slprintf(buf1, sizeof(buf1) - 1,"%-15.15s%c",name->name,name->name_type); + } + + buf[offset] = 0x20; + + ret = 34; + + for (m=0;m<16;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; + pstrcpy(&buf[offset+1],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]; + char *p = ret[i]; + + if (!n->scope[0]) + slprintf(p,sizeof(fstring)-1, "%s<%02x>",n->name,n->name_type); + else + slprintf(p,sizeof(fstring)-1, "%s<%02x>.%s",n->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 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> 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) +{ + 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->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; + struct in_addr addr; + int port; + + length = read_udp_socket(fd, buf, sizeof(buf), &addr, &port); + if (length < MIN_DGRAM_SIZE) return(NULL); + + packet = parse_packet(buf, length, packet_type); + if (!packet) return NULL; + + packet->fd = fd; + packet->ip = addr; + packet->port = port; + + 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))); + + 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; + uchar *ubuf = (uchar *)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) +{ + memset( (char *)n, '\0', sizeof(struct nmb_name) ); + push_ascii(n->name, name, 16, STR_TERMINATE|STR_UPPER); + n->name_type = (unsigned int)type & 0xFF; + StrnCpy( n->scope, lp_netbios_scope(), 63 ); + strupper( n->scope ); +} + +/******************************************************************* + 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; + uchar *ubuf = (uchar *)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(uchar *p1, uchar *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 uchar sort_ip[4]; + +/**************************************************************************** +compare two query reply records + ***************************************************************************/ +static int name_query_comp(uchar *p1, uchar *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(char *dns_name) +{ + static char netbios_name[16]; + int i; + StrnCpy(netbios_name, dns_name, 15); + 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 = 15; i >= 0; i--) + { + if (netbios_name[i] == '.') + { + netbios_name[i] = 0; + break; + } + } +#endif /* TRUNCATE_NETBIOS_NAME */ + + return netbios_name; +} + + +/**************************************************************************** +interpret the weird netbios "name". Return the name type +****************************************************************************/ +static int name_interpret(char *in,char *out) +{ + int ret; + int len = (*in++) / 2; + + *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++; + } + *out = 0; + ret = out[-1]; + +#ifdef NETBIOS_SCOPE + /* Handle any scope names */ + while(*in) + { + *out++ = '.'; /* Scope names are separated by periods */ + len = *(uchar *)in++; + StrnCpy(out, in, len); + out += len; + *out=0; + in += len; + } +#endif + return(ret); +} + + +/**************************************************************************** +return the number of bytes that would be occupied by the result of +name_mangle() +****************************************************************************/ +uint_t nbt_mangled_name_len(void) +{ + const char *scope = lp_netbios_scope(); + uint_t ret = 34; + if (scope && *scope) { + ret += strlen(scope) + 1; + } + return ret; +} + +/**************************************************************************** +mangle a name into netbios format + + Note: must be nbt_mangled_name_len() in length +****************************************************************************/ +int name_mangle(char *In, char *Out, char name_type) +{ + int i; + int c; + int len; + char buf[20]; + char *p = Out; + const char *scope = lp_netbios_scope(); + + /* Safely copy the input string, In, into buf[]. */ + memset( buf, 0, 20 ); + if (strcmp(In,"*") == 0) { + buf[0] = '*'; + } else { + slprintf( buf, sizeof(buf) - 1, "%-15.15s%c", In, 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 < 16; i++ ) { + c = toupper( buf[i] ); + p[i*2] = ( (c >> 4) & 0xF ) + 'A'; + p[(i*2)+1] = (c & 0xF) + 'A'; + } + p += 32; + p[0] = '\0'; + + if (!scope || !*scope) { + return name_len(Out); + } + + /* Add the scope string. */ + for (i = 0, len = 0; scope[i]; i++, len++) { + switch(scope[i]) { + case '.': + p[0] = len; + p += (len + 1); + len = -1; + break; + default: + p[len+1] = scope[i]; + break; + } + } + + p[0] = len; + if (len > 0) { + p[len+1] = 0; + } + + return name_len(Out); +} + +/**************************************************************************** +find a pointer to a netbios name +****************************************************************************/ +static char *name_ptr(char *buf,int ofs) +{ + uchar c = *(uchar *)(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 +****************************************************************************/ +int name_extract(char *buf,int ofs,char *name) +{ + char *p = name_ptr(buf,ofs); + int d = PTR_DIFF(p,buf+ofs); + pstrcpy(name,""); + 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 */ + uchar *s = (uchar *)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); +} /* name_len */ diff --git a/source4/libcli/ntlmssp.c b/source4/libcli/ntlmssp.c new file mode 100644 index 0000000000..c4ad260a1a --- /dev/null +++ b/source4/libcli/ntlmssp.c @@ -0,0 +1,625 @@ +/* + 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" + +/** + * 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(struct ntlmssp_state *ntlmssp_state) +{ + static uchar chal[8]; + generate_random_buffer(chal, sizeof(chal), False); + + return chal; +} + +/** + * Determine correct target name flags for reply, given server role + * and negoitated falgs + * + * @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 ""; + } +} + +/** + * 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 ntlmssp_command, neg_flags, 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 (!msrpc_parse(&request, "CddAA", + "NTLMSSP", + &ntlmssp_command, + &neg_flags, + &cliname, + &domname)) { + return NT_STATUS_INVALID_PARAMETER; + } + + SAFE_FREE(cliname); + SAFE_FREE(domname); + + debug_ntlmssp_flags(neg_flags); + + cryptkey = ntlmssp_state->get_challenge(ntlmssp_state); + + data_blob_free(&ntlmssp_state->chal); + ntlmssp_state->chal = data_blob(cryptkey, 8); + + /* Give them the challenge. For now, ignore neg_flags and just + return the flags we want. Obviously this is not correct */ + + chal_flags = + NTLMSSP_NEGOTIATE_128 | + NTLMSSP_NEGOTIATE_NTLM; + + if (neg_flags & NTLMSSP_NEGOTIATE_UNICODE) { + chal_flags |= NTLMSSP_NEGOTIATE_UNICODE; + ntlmssp_state->unicode = True; + } else { + chal_flags |= NTLMSSP_NEGOTIATE_OEM; + } + + target_name = ntlmssp_target_name(ntlmssp_state, + neg_flags, &chal_flags); + + /* This should be a 'netbios domain -> DNS domain' mapping */ + dnsdomname[0] = '\0'; + get_mydomname(dnsdomname); + strlower(dnsdomname); + + dnsname[0] = '\0'; + get_myfullname(dnsname); + strlower(dnsname); + + 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; + } + + /* the numbers here are the string type flags */ + msrpc_gen(&struct_blob, "aaaaa", + ntlmssp_state->unicode, NTLMSSP_NAME_TYPE_DOMAIN, target_name, + ntlmssp_state->unicode, NTLMSSP_NAME_TYPE_SERVER, ntlmssp_state->get_global_myname(), + ntlmssp_state->unicode, NTLMSSP_NAME_TYPE_DOMAIN_DNS, target_name_dns, + ntlmssp_state->unicode, NTLMSSP_NAME_TYPE_SERVER_DNS, dnsdomname, + ntlmssp_state->unicode, 0, ""); + } else { + struct_blob = data_blob(NULL, 0); + } + + { + 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 sess_key; + uint32 ntlmssp_command, neg_flags; + NTSTATUS nt_status; + + const char *parse_string; + + /* parse the NTLMSSP packet */ +#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); + + SAFE_FREE(ntlmssp_state->user); + SAFE_FREE(ntlmssp_state->domain); + SAFE_FREE(ntlmssp_state->workstation); + + /* now the NTLMSSP encoded auth hashes */ + if (!msrpc_parse(&request, parse_string, + "NTLMSSP", + &ntlmssp_command, + &ntlmssp_state->lm_resp, + &ntlmssp_state->nt_resp, + &ntlmssp_state->domain, + &ntlmssp_state->user, + &ntlmssp_state->workstation, + &sess_key, + &neg_flags)) { + return NT_STATUS_INVALID_PARAMETER; + } + + data_blob_free(&sess_key); + + DEBUG(3,("Got user=[%s] domain=[%s] workstation=[%s] len1=%d len2=%d\n", + ntlmssp_state->user, ntlmssp_state->domain, ntlmssp_state->workstation, ntlmssp_state->lm_resp.length, 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 + + nt_status = ntlmssp_state->check_password(ntlmssp_state); + + *reply = data_blob(NULL, 0); + + return nt_status; +} + +/** + * Create an NTLMSSP state machine + * + * @param ntlmssp_state NTLMSSP State, allocated by this funciton + */ + +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)->mem_ctx = mem_ctx; + (*ntlmssp_state)->get_challenge = get_challenge; + + (*ntlmssp_state)->get_global_myname = lp_netbios_name; + (*ntlmssp_state)->get_domain = lp_workgroup; + (*ntlmssp_state)->server_role = ROLE_DOMAIN_MEMBER; /* a good default */ + + (*ntlmssp_state)->expected_state = NTLMSSP_NEGOTIATE; + + return NT_STATUS_OK; +} + +/** + * End an NTLMSSP state machine + * + * @param ntlmssp_state NTLMSSP State, free()ed by this funciton + */ + +NTSTATUS ntlmssp_server_end(NTLMSSP_STATE **ntlmssp_state) +{ + TALLOC_CTX *mem_ctx = (*ntlmssp_state)->mem_ctx; + + data_blob_free(&(*ntlmssp_state)->chal); + data_blob_free(&(*ntlmssp_state)->lm_resp); + data_blob_free(&(*ntlmssp_state)->nt_resp); + + SAFE_FREE((*ntlmssp_state)->user); + SAFE_FREE((*ntlmssp_state)->domain); + SAFE_FREE((*ntlmssp_state)->workstation); + + talloc_destroy(mem_ctx); + *ntlmssp_state = NULL; + return NT_STATUS_OK; +} + +/** + * Next state function for the NTLMSSP state machine + * + * @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, NT_STATUS_MORE_PROCESSING_REQUIRED or NT_STATUS_OK. + */ + +NTSTATUS ntlmssp_server_update(NTLMSSP_STATE *ntlmssp_state, + const DATA_BLOB request, DATA_BLOB *reply) +{ + uint32 ntlmssp_command; + *reply = data_blob(NULL, 0); + + if (!msrpc_parse(&request, "Cd", + "NTLMSSP", + &ntlmssp_command)) { + return NT_STATUS_INVALID_PARAMETER; + } + + if (ntlmssp_command != ntlmssp_state->expected_state) { + return NT_STATUS_INVALID_PARAMETER; + } + + if (ntlmssp_command == NTLMSSP_NEGOTIATE) { + return ntlmssp_server_negotiate(ntlmssp_state, request, reply); + } else if (ntlmssp_command == NTLMSSP_AUTH) { + return ntlmssp_server_auth(ntlmssp_state, request, reply); + } else { + return NT_STATUS_INVALID_PARAMETER; + } +} + +/********************************************************************* + 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_client_state *ntlmssp_state, + DATA_BLOB reply, DATA_BLOB *next_request) +{ + if (ntlmssp_state->unicode) { + ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_UNICODE; + } + + /* 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()); + + 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_client_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; + 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); + uint8 datagram_sess_key[16]; + + ZERO_STRUCT(datagram_sess_key); + + if (!msrpc_parse(&reply, "CdBd", + "NTLMSSP", + &ntlmssp_command, + &server_domain_blob, + &chal_flags)) { + DEBUG(0, ("Failed to parse the NTLMSSP Challenge\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + data_blob_free(&server_domain_blob); + + if (chal_flags & NTLMSSP_NEGOTIATE_UNICODE) { + chal_parse_string = "CdUdbddB"; + auth_gen_string = "CdBBUUUBd"; + ntlmssp_state->unicode = True; + ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_UNICODE; + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_OEM; + } else if (chal_flags & NTLMSSP_NEGOTIATE_OEM) { + chal_parse_string = "CdAdbddB"; + auth_gen_string = "CdBBAAABd"; + ntlmssp_state->unicode = False; + ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_UNICODE; + ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_OEM; + } else { + return NT_STATUS_INVALID_PARAMETER; + } + + if (!msrpc_parse(&reply, chal_parse_string, + "NTLMSSP", + &ntlmssp_command, + &server_domain, + &chal_flags, + &challenge_blob, 8, + &unkn1, &unkn2, + &struct_blob)) { + DEBUG(0, ("Failed to parse the NTLMSSP Challenge\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + SAFE_FREE(server_domain); + data_blob_free(&struct_blob); + + if (challenge_blob.length != 8) { + return NT_STATUS_INVALID_PARAMETER; + } + + if (ntlmssp_state->use_ntlmv2) { + + /* 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, + &lm_response, &nt_response, &session_key)) { + data_blob_free(&challenge_blob); + return NT_STATUS_NO_MEMORY; + } + } else { + uchar nt_hash[16]; + E_md4hash(ntlmssp_state->password, nt_hash); + + /* non encrypted password supplied. Ignore ntpass. */ + if (lp_client_lanman_auth()) { + lm_response = data_blob(NULL, 24); + SMBencrypt(ntlmssp_state->password,challenge_blob.data, + lm_response.data); + } + + nt_response = data_blob(NULL, 24); + SMBNTencrypt(ntlmssp_state->password,challenge_blob.data, + nt_response.data); + session_key = data_blob(NULL, 16); + SMBsesskeygen_ntv1(nt_hash, NULL, session_key.data); + } + + data_blob_free(&challenge_blob); + + /* 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(), + datagram_sess_key, 0, + ntlmssp_state->neg_flags)) { + + data_blob_free(&lm_response); + data_blob_free(&nt_response); + data_blob_free(&session_key); + return NT_STATUS_NO_MEMORY; + } + + data_blob_free(&lm_response); + data_blob_free(&nt_response); + + ntlmssp_state->session_key = session_key; + + return NT_STATUS_MORE_PROCESSING_REQUIRED; +} + +NTSTATUS ntlmssp_client_start(NTLMSSP_CLIENT_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_server_start: talloc failed!\n")); + talloc_destroy(mem_ctx); + return NT_STATUS_NO_MEMORY; + } + + (*ntlmssp_state)->mem_ctx = mem_ctx; + + (*ntlmssp_state)->get_global_myname = lp_netbios_name; + (*ntlmssp_state)->get_domain = lp_workgroup; + + (*ntlmssp_state)->unicode = True; + + (*ntlmssp_state)->neg_flags = + NTLMSSP_NEGOTIATE_128 | + NTLMSSP_NEGOTIATE_NTLM | + NTLMSSP_REQUEST_TARGET; + + return NT_STATUS_OK; +} + +NTSTATUS ntlmssp_client_end(NTLMSSP_CLIENT_STATE **ntlmssp_state) +{ + TALLOC_CTX *mem_ctx = (*ntlmssp_state)->mem_ctx; + + data_blob_free(&(*ntlmssp_state)->session_key); + talloc_destroy(mem_ctx); + *ntlmssp_state = NULL; + return NT_STATUS_OK; +} + +NTSTATUS ntlmssp_client_update(NTLMSSP_CLIENT_STATE *ntlmssp_state, + DATA_BLOB reply, DATA_BLOB *next_request) +{ + uint32 ntlmssp_command; + *next_request = data_blob(NULL, 0); + + if (!reply.length) { + return ntlmssp_client_initial(ntlmssp_state, reply, next_request); + } + + if (!msrpc_parse(&reply, "Cd", + "NTLMSSP", + &ntlmssp_command)) { + return NT_STATUS_INVALID_PARAMETER; + } + + if (ntlmssp_command == NTLMSSP_CHALLENGE) { + return ntlmssp_client_challenge(ntlmssp_state, reply, next_request); + } + return NT_STATUS_INVALID_PARAMETER; +} + +NTSTATUS ntlmssp_set_username(NTLMSSP_CLIENT_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; +} + +NTSTATUS ntlmssp_set_password(NTLMSSP_CLIENT_STATE *ntlmssp_state, const char *password) +{ + ntlmssp_state->password = talloc_strdup(ntlmssp_state->mem_ctx, password); + if (!ntlmssp_state->password) { + return NT_STATUS_NO_MEMORY; + } + return NT_STATUS_OK; +} + +NTSTATUS ntlmssp_set_domain(NTLMSSP_CLIENT_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; +} diff --git a/source4/libcli/ntlmssp_parse.c b/source4/libcli/ntlmssp_parse.c new file mode 100644 index 0000000000..ac779a3906 --- /dev/null +++ b/source4/libcli/ntlmssp_parse.c @@ -0,0 +1,303 @@ +/* + Unix SMB/CIFS implementation. + simple kerberos5/SPNEGO routines + Copyright (C) Andrew Tridgell 2001 + Copyright (C) Jim McDonough 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 BOOL unicode, 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; + BOOL unicode; + + /* 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': + unicode = va_arg(ap, BOOL); + n = va_arg(ap, int); + s = va_arg(ap, char *); + if (unicode) { + data_size += (str_charnum(s) * 2) + 4; + } else { + data_size += (str_ascii_charnum(s)) + 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': + unicode = va_arg(ap, BOOL); + n = va_arg(ap, int); + SSVAL(blob->data, data_ofs, n); data_ofs += 2; + s = va_arg(ap, char *); + if (unicode) { + 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; + } else { + n = str_ascii_charnum(s); + SSVAL(blob->data, data_ofs, n); data_ofs += 2; + if (0 < n) { + push_string(NULL, blob->data+data_ofs, s, n, + STR_ASCII|STR_NOALIGN); + } + data_ofs += n; + } + 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; + 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; + + /* make sure its in the right format - be strict */ + if (len1 != len2 || ptr + len1 > blob->length) { + return False; + } + if (len1 & 1) { + /* if odd length and unicode */ + return False; + } + + ps = va_arg(ap, char **); + 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; + + /* make sure its in the right format - be strict */ + if (len1 != len2 || ptr + len1 > blob->length) { + return False; + } + + ps = va_arg(ap, char **); + 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; + /* make sure its in the right format - be strict */ + if (len1 != len2 || ptr + len1 > blob->length) { + return False; + } + b = (DATA_BLOB *)va_arg(ap, void *); + *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); + *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 *); + 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/source4/libcli/raw/README b/source4/libcli/raw/README new file mode 100644 index 0000000000..cb3e507e3a --- /dev/null +++ b/source4/libcli/raw/README @@ -0,0 +1,5 @@ +Design notes for client library restructure: + +1 - no references to cli_state should exist in libcli/raw. +2 - all interfaces to functions in this directory should use cli_session or cli_tree as + the primary context structure \ No newline at end of file diff --git a/source4/libcli/raw/clikrb5.c b/source4/libcli/raw/clikrb5.c new file mode 100644 index 0000000000..5edc56daa9 --- /dev/null +++ b/source4/libcli/raw/clikrb5.c @@ -0,0 +1,399 @@ +/* + 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 = (char *)&(((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); + return krb5_string_to_key(context, &eblock, key, password, &salt); +} +#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 krb5_mk_req2(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 */ + memset((char *)&creds, 0, sizeof(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 +*/ +DATA_BLOB krb5_get_ticket(const char *principal, time_t time_offset) +{ + krb5_error_code retval; + krb5_data packet; + krb5_ccache ccdef; + krb5_context context; + krb5_auth_context auth_context = NULL; + DATA_BLOB ret; + 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 = krb5_mk_req2(context, + &auth_context, + 0, + principal, + ccdef, &packet))) { + goto failed; + } + + ret = data_blob(packet.data, packet.length); +/* Hmm, heimdal dooesn't have this - what's the correct call? */ +/* krb5_free_data_contents(context, &packet); */ + krb5_free_context(context); + return ret; + +failed: + if ( context ) + krb5_free_context(context); + + return data_blob(NULL, 0); +} + + BOOL krb5_get_smb_session_key(krb5_context context, krb5_auth_context auth_context, uint8 session_key[16]) + { +#ifdef ENCTYPE_ARCFOUR_HMAC + krb5_keyblock *skey; +#endif + BOOL ret = False; + + memset(session_key, 0, 16); + +#ifdef ENCTYPE_ARCFOUR_HMAC + if (krb5_auth_con_getremotesubkey(context, auth_context, &skey) == 0 && skey != NULL) { + if (KRB5_KEY_TYPE(skey) == + ENCTYPE_ARCFOUR_HMAC + && KRB5_KEY_LENGTH(skey) == 16) { + memcpy(session_key, KRB5_KEY_DATA(skey), KRB5_KEY_LENGTH(skey)); + ret = True; + } + krb5_free_keyblock(context, skey); + } +#endif /* ENCTYPE_ARCFOUR_HMAC */ + + return ret; + } +#else /* HAVE_KRB5 */ + /* this saves a few linking headaches */ +DATA_BLOB krb5_get_ticket(const char *principal, time_t time_offset) + { + DEBUG(0,("NO KERBEROS SUPPORT\n")); + return data_blob(NULL, 0); + } + +#endif diff --git a/source4/libcli/raw/clioplock.c b/source4/libcli/raw/clioplock.c new file mode 100644 index 0000000000..8f69716bda --- /dev/null +++ b/source4/libcli/raw/clioplock.c @@ -0,0 +1,57 @@ +/* + 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. +*/ + +#include "includes.h" + +/**************************************************************************** +send an ack for an oplock break request +****************************************************************************/ +BOOL cli_oplock_ack(struct cli_tree *tree, uint16 fnum, uint16 ack_level) +{ + BOOL ret; + struct cli_request *req; + + req = cli_request_setup(tree, SMBlockingX, 8, 0); + + SSVAL(req->out.vwv,VWV(0),0xFF); + SSVAL(req->out.vwv,VWV(1),0); + SSVAL(req->out.vwv,VWV(2),fnum); + SSVAL(req->out.vwv,VWV(3),ack_level); + SIVAL(req->out.vwv,VWV(4),0); + SSVAL(req->out.vwv,VWV(6),0); + SSVAL(req->out.vwv,VWV(7),0); + + ret = cli_request_send(req); + cli_request_destroy(req); + + return ret; +} + + +/**************************************************************************** +set the oplock handler for a connection +****************************************************************************/ +void cli_oplock_handler(struct cli_transport *transport, + BOOL (*handler)(struct cli_transport *, uint16, uint16, uint8, void *), + void *private) +{ + transport->oplock.handler = handler; + transport->oplock.private = private; +} diff --git a/source4/libcli/raw/clirewrite.c b/source4/libcli/raw/clirewrite.c new file mode 100644 index 0000000000..2d2e2e3feb --- /dev/null +++ b/source4/libcli/raw/clirewrite.c @@ -0,0 +1,22 @@ +#include "includes.h" + +/* + + this is a set of temporary stub functions used during the libsmb rewrite. + This file will need to go away before the rewrite is complete. +*/ + +void become_root(void) +{} + +void unbecome_root(void) +{} + +BOOL become_user_permanently(uid_t uid, gid_t gid) +{ return True; } + +void set_effective_uid(uid_t uid) +{} + +uid_t sec_initial_uid(void) +{ return 0; } diff --git a/source4/libcli/raw/clisession.c b/source4/libcli/raw/clisession.c new file mode 100644 index 0000000000..406491e432 --- /dev/null +++ b/source4/libcli/raw/clisession.c @@ -0,0 +1,444 @@ +/* + Unix SMB/CIFS implementation. + SMB client session context management functions + Copyright (C) Andrew Tridgell 1994-1998 + Copyright (C) James Myers 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 SETUP_REQUEST_SESSION(cmd, wct, buflen) do { \ + req = cli_request_setup_session(session, cmd, wct, buflen); \ + if (!req) return NULL; \ +} while (0) + +/**************************************************************************** + Initialize the session context +****************************************************************************/ +struct cli_session *cli_session_init(struct cli_transport *transport) +{ + struct cli_session *session; + TALLOC_CTX *mem_ctx = talloc_init("cli_session"); + if (mem_ctx == NULL) { + return NULL; + } + + session = talloc_zero(mem_ctx, sizeof(*session)); + if (!session) { + talloc_destroy(mem_ctx); + return NULL; + } + + session->mem_ctx = mem_ctx; + session->transport = transport; + session->pid = (uint16)getpid(); + session->vuid = UID_FIELD_INVALID; + session->transport->reference_count++; + + return session; +} + +/**************************************************************************** +reduce reference_count and destroy is <= 0 +****************************************************************************/ +void cli_session_close(struct cli_session *session) +{ + session->reference_count--; + if (session->reference_count <= 0) { + cli_transport_close(session->transport); + talloc_destroy(session->mem_ctx); + } +} + +/**************************************************************************** + Perform a session setup (async send) +****************************************************************************/ +struct cli_request *smb_raw_session_setup_send(struct cli_session *session, union smb_sesssetup *parms) +{ + struct cli_request *req; + + switch (parms->generic.level) { + case RAW_SESSSETUP_GENERIC: + /* handled elsewhere */ + return NULL; + + case RAW_SESSSETUP_OLD: + SETUP_REQUEST_SESSION(SMBsesssetupX, 10, 0); + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + SSVAL(req->out.vwv,VWV(2),parms->old.in.bufsize); + SSVAL(req->out.vwv,VWV(3),parms->old.in.mpx_max); + SSVAL(req->out.vwv,VWV(4),parms->old.in.vc_num); + SIVAL(req->out.vwv,VWV(5),parms->old.in.sesskey); + SSVAL(req->out.vwv,VWV(7),parms->old.in.password.length); + cli_req_append_blob(req, &parms->old.in.password); + cli_req_append_string(req, parms->old.in.user, STR_TERMINATE); + cli_req_append_string(req, parms->old.in.domain, STR_TERMINATE|STR_UPPER); + cli_req_append_string(req, parms->old.in.os, STR_TERMINATE); + cli_req_append_string(req, parms->old.in.lanman, STR_TERMINATE); + break; + + case RAW_SESSSETUP_NT1: + SETUP_REQUEST_SESSION(SMBsesssetupX, 13, 0); + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + SSVAL(req->out.vwv, VWV(2), parms->nt1.in.bufsize); + SSVAL(req->out.vwv, VWV(3), parms->nt1.in.mpx_max); + SSVAL(req->out.vwv, VWV(4), parms->nt1.in.vc_num); + SIVAL(req->out.vwv, VWV(5), parms->nt1.in.sesskey); + SSVAL(req->out.vwv, VWV(7), parms->nt1.in.password1.length); + SSVAL(req->out.vwv, VWV(8), parms->nt1.in.password2.length); + SIVAL(req->out.vwv, VWV(9), 0); /* reserved */ + SIVAL(req->out.vwv, VWV(11), parms->nt1.in.capabilities); + cli_req_append_blob(req, &parms->nt1.in.password1); + cli_req_append_blob(req, &parms->nt1.in.password2); + cli_req_append_string(req, parms->nt1.in.user, STR_TERMINATE); + cli_req_append_string(req, parms->nt1.in.domain, STR_TERMINATE|STR_UPPER); + cli_req_append_string(req, parms->nt1.in.os, STR_TERMINATE); + cli_req_append_string(req, parms->nt1.in.lanman, STR_TERMINATE); + break; + + case RAW_SESSSETUP_SPNEGO: + SETUP_REQUEST_SESSION(SMBsesssetupX, 12, 0); + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + SSVAL(req->out.vwv, VWV(2), parms->spnego.in.bufsize); + SSVAL(req->out.vwv, VWV(3), parms->spnego.in.mpx_max); + SSVAL(req->out.vwv, VWV(4), parms->spnego.in.vc_num); + SIVAL(req->out.vwv, VWV(5), parms->spnego.in.sesskey); + SSVAL(req->out.vwv, VWV(7), parms->spnego.in.secblob.length); + SIVAL(req->out.vwv, VWV(10), parms->spnego.in.capabilities); + cli_req_append_blob(req, &parms->spnego.in.secblob); + cli_req_append_string(req, parms->spnego.in.os, STR_TERMINATE); + cli_req_append_string(req, parms->spnego.in.lanman, STR_TERMINATE); + break; + } + + if (!cli_request_send(req)) { + cli_request_destroy(req); + return NULL; + } + + return req; +} + + +/**************************************************************************** + Perform a session setup (async recv) +****************************************************************************/ +NTSTATUS smb_raw_session_setup_recv(struct cli_request *req, + TALLOC_CTX *mem_ctx, + union smb_sesssetup *parms) +{ + uint16 len; + char *p; + + if (!cli_request_receive(req)) { + return cli_request_destroy(req); + } + + if (!NT_STATUS_IS_OK(req->status) && + !NT_STATUS_EQUAL(req->status,NT_STATUS_MORE_PROCESSING_REQUIRED)) { + return cli_request_destroy(req); + } + + switch (parms->generic.level) { + case RAW_SESSSETUP_GENERIC: + /* handled elsewhere */ + return NT_STATUS_INVALID_LEVEL; + + case RAW_SESSSETUP_OLD: + CLI_CHECK_WCT(req, 3); + ZERO_STRUCT(parms->old.out); + parms->old.out.vuid = SVAL(req->in.hdr, HDR_UID); + parms->old.out.action = SVAL(req->in.vwv, VWV(2)); + p = req->in.data; + if (p) { + p += cli_req_pull_string(req, mem_ctx, &parms->old.out.os, p, -1, STR_TERMINATE); + p += cli_req_pull_string(req, mem_ctx, &parms->old.out.lanman, p, -1, STR_TERMINATE); + p += cli_req_pull_string(req, mem_ctx, &parms->old.out.domain, p, -1, STR_TERMINATE); + } + break; + + case RAW_SESSSETUP_NT1: + CLI_CHECK_WCT(req, 3); + ZERO_STRUCT(parms->nt1.out); + parms->nt1.out.vuid = SVAL(req->in.hdr, HDR_UID); + parms->nt1.out.action = SVAL(req->in.vwv, VWV(2)); + p = req->in.data; + if (p) { + p += cli_req_pull_string(req, mem_ctx, &parms->nt1.out.os, p, -1, STR_TERMINATE); + p += cli_req_pull_string(req, mem_ctx, &parms->nt1.out.lanman, p, -1, STR_TERMINATE); + if (p < (req->in.data + req->in.data_size)) { + p += cli_req_pull_string(req, mem_ctx, &parms->nt1.out.domain, p, -1, STR_TERMINATE); + } + } + break; + + case RAW_SESSSETUP_SPNEGO: + CLI_CHECK_WCT(req, 4); + ZERO_STRUCT(parms->spnego.out); + parms->spnego.out.vuid = SVAL(req->in.hdr, HDR_UID); + parms->spnego.out.action = SVAL(req->in.vwv, VWV(2)); + len = SVAL(req->in.vwv, VWV(3)); + p = req->in.data; + if (!p) { + break; + } + + parms->spnego.out.secblob = cli_req_pull_blob(req, mem_ctx, p, len); + p += parms->spnego.out.secblob.length; + p += cli_req_pull_string(req, mem_ctx, &parms->spnego.out.os, p, -1, STR_TERMINATE); + p += cli_req_pull_string(req, mem_ctx, &parms->spnego.out.lanman, p, -1, STR_TERMINATE); + p += cli_req_pull_string(req, mem_ctx, &parms->spnego.out.domain, p, -1, STR_TERMINATE); + break; + } + +failed: + return cli_request_destroy(req); +} + +/* + form an encrypted lanman password from a plaintext password + and the server supplied challenge +*/ +static DATA_BLOB lanman_blob(const char *pass, DATA_BLOB challenge) +{ + DATA_BLOB blob = data_blob(NULL, 24); + SMBencrypt(pass, challenge.data, blob.data); + return blob; +} + +/* + form an encrypted NT password from a plaintext password + and the server supplied challenge +*/ +static DATA_BLOB nt_blob(const char *pass, DATA_BLOB challenge) +{ + DATA_BLOB blob = data_blob(NULL, 24); + SMBNTencrypt(pass, challenge.data, blob.data); + return blob; +} + +/* + setup signing for a NT1 style session setup +*/ +static void setup_nt1_signing(struct cli_transport *transport, const char *password) +{ + uchar nt_hash[16]; + uchar session_key[16]; + DATA_BLOB nt_response; + + E_md4hash(password, nt_hash); + SMBsesskeygen_ntv1(nt_hash, NULL, session_key); + nt_response = nt_blob(password, transport->negotiate.secblob); + + cli_transport_simple_set_signing(transport, session_key, nt_response); +} + +/**************************************************************************** + Perform a session setup (sync interface) using generic interface and the old + style sesssetup call +****************************************************************************/ +static NTSTATUS smb_raw_session_setup_generic_old(struct cli_session *session, + TALLOC_CTX *mem_ctx, + union smb_sesssetup *parms) +{ + NTSTATUS status; + union smb_sesssetup s2; + + /* use the old interface */ + s2.generic.level = RAW_SESSSETUP_OLD; + s2.old.in.bufsize = ~0; + s2.old.in.mpx_max = 50; + s2.old.in.vc_num = 1; + s2.old.in.sesskey = parms->generic.in.sesskey; + s2.old.in.domain = parms->generic.in.domain; + s2.old.in.user = parms->generic.in.user; + s2.old.in.os = "Unix"; + s2.old.in.lanman = "Samba"; + + if (session->transport->negotiate.sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) { + s2.old.in.password = lanman_blob(parms->generic.in.password, + session->transport->negotiate.secblob); + } else { + s2.old.in.password = data_blob(parms->generic.in.password, + strlen(parms->generic.in.password)); + } + + status = smb_raw_session_setup(session, mem_ctx, &s2); + + data_blob_free(&s2.old.in.password); + + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + parms->generic.out.vuid = s2.old.out.vuid; + parms->generic.out.os = s2.old.out.os; + parms->generic.out.lanman = s2.old.out.lanman; + parms->generic.out.domain = s2.old.out.domain; + + return NT_STATUS_OK; +} + +/**************************************************************************** + Perform a session setup (sync interface) using generic interface and the NT1 + style sesssetup call +****************************************************************************/ +static NTSTATUS smb_raw_session_setup_generic_nt1(struct cli_session *session, + TALLOC_CTX *mem_ctx, + union smb_sesssetup *parms) +{ + NTSTATUS status; + union smb_sesssetup s2; + + s2.generic.level = RAW_SESSSETUP_NT1; + s2.nt1.in.bufsize = ~0; + s2.nt1.in.mpx_max = 50; + s2.nt1.in.vc_num = 1; + s2.nt1.in.sesskey = parms->generic.in.sesskey; + s2.nt1.in.capabilities = parms->generic.in.capabilities; + s2.nt1.in.domain = parms->generic.in.domain; + s2.nt1.in.user = parms->generic.in.user; + s2.nt1.in.os = "Unix"; + s2.nt1.in.lanman = "Samba"; + + if (session->transport->negotiate.sec_mode & + NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) { + s2.nt1.in.password1 = lanman_blob(parms->generic.in.password, + session->transport->negotiate.secblob); + s2.nt1.in.password2 = nt_blob(parms->generic.in.password, + session->transport->negotiate.secblob); + setup_nt1_signing(session->transport, parms->generic.in.password); + } else { + s2.nt1.in.password1 = data_blob(parms->generic.in.password, + strlen(parms->generic.in.password)); + s2.nt1.in.password2 = data_blob(NULL, 0); + } + + status = smb_raw_session_setup(session, mem_ctx, &s2); + + data_blob_free(&s2.nt1.in.password1); + data_blob_free(&s2.nt1.in.password2); + + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + parms->generic.out.vuid = s2.nt1.out.vuid; + parms->generic.out.os = s2.nt1.out.os; + parms->generic.out.lanman = s2.nt1.out.lanman; + parms->generic.out.domain = s2.nt1.out.domain; + + return NT_STATUS_OK; +} + + +/**************************************************************************** + Perform a session setup (sync interface) using generic interface +****************************************************************************/ +static NTSTATUS smb_raw_session_setup_generic(struct cli_session *session, + TALLOC_CTX *mem_ctx, + union smb_sesssetup *parms) +{ + if (session->transport->negotiate.protocol < PROTOCOL_LANMAN1) { + /* no session setup at all in earliest protocols */ + ZERO_STRUCT(parms->generic.out); + return NT_STATUS_OK; + } + + /* see if we need to use the original session setup interface */ + if (session->transport->negotiate.protocol < PROTOCOL_NT1) { + return smb_raw_session_setup_generic_old(session, mem_ctx, parms); + } + + /* see if we should use the NT1 interface */ + if (!(session->transport->negotiate.capabilities & CAP_EXTENDED_SECURITY) || + !session->transport->options.use_spnego) { + return smb_raw_session_setup_generic_nt1(session, mem_ctx, parms); + } + + /* default to using SPNEGO/NTLMSSP */ + DEBUG(0,("Need to add client SPNEGO code back in\n")); + return NT_STATUS_UNSUCCESSFUL; +} + + +/**************************************************************************** + Perform a session setup (sync interface) +this interface allows for RAW_SESSSETUP_GENERIC to auto-select session +setup varient based on negotiated protocol options +****************************************************************************/ +NTSTATUS smb_raw_session_setup(struct cli_session *session, TALLOC_CTX *mem_ctx, + union smb_sesssetup *parms) +{ + struct cli_request *req; + + if (parms->generic.level == RAW_SESSSETUP_GENERIC) { + return smb_raw_session_setup_generic(session, mem_ctx, parms); + } + + req = smb_raw_session_setup_send(session, parms); + return smb_raw_session_setup_recv(req, mem_ctx, parms); +} + + +/**************************************************************************** + Send a uloggoff (async send) +*****************************************************************************/ +struct cli_request *smb_raw_ulogoff_send(struct cli_session *session) +{ + struct cli_request *req; + + SETUP_REQUEST_SESSION(SMBulogoffX, 2, 0); + + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + + if (!cli_request_send(req)) { + cli_request_destroy(req); + return NULL; + } + + return req; +} + +/**************************************************************************** + Send a uloggoff (sync interface) +*****************************************************************************/ +NTSTATUS smb_raw_ulogoff(struct cli_session *session) +{ + struct cli_request *req = smb_raw_ulogoff_send(session); + return cli_request_simple_recv(req); +} + + +/**************************************************************************** + Send a SMBexit +****************************************************************************/ +NTSTATUS smb_raw_exit(struct cli_session *session) +{ + struct cli_request *req; + + req = cli_request_setup_session(session, SMBexit, 0, 0); + + if (cli_request_send(req)) { + cli_request_receive(req); + } + return cli_request_destroy(req); +} diff --git a/source4/libcli/raw/clisocket.c b/source4/libcli/raw/clisocket.c new file mode 100644 index 0000000000..f0e05085c4 --- /dev/null +++ b/source4/libcli/raw/clisocket.c @@ -0,0 +1,148 @@ +/* + Unix SMB/CIFS implementation. + SMB client socket context management functions + Copyright (C) Andrew Tridgell 1994-1998 + Copyright (C) James Myers 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 cli_socket context +*/ +struct cli_socket *cli_sock_init(void) +{ + struct cli_socket *sock; + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_init("cli_socket"); + if (!mem_ctx) return NULL; + + sock = talloc_zero(mem_ctx, sizeof(*sock)); + if (!sock) { + talloc_destroy(mem_ctx); + return NULL; + } + + sock->mem_ctx = mem_ctx; + sock->fd = -1; + sock->port = 445; + /* 20 second default timeout */ + sock->timeout = 20000; + + return sock; +} + +/* + connect a cli_socket context to an IP/port pair + if port is 0 then choose 445 then 139 +*/ +BOOL cli_sock_connect(struct cli_socket *sock, struct in_addr *ip, int port) +{ + if (getenv("LIBSMB_PROG")) { + sock->fd = sock_exec(getenv("LIBSMB_PROG")); + return sock->fd != -1; + } + + if (port == 0) { + return cli_sock_connect(sock, ip, 445) || + cli_sock_connect(sock, ip, 139); + } + + sock->dest_ip = *ip; + sock->port = port; + sock->fd = open_socket_out(SOCK_STREAM, + &sock->dest_ip, + sock->port, + LONG_CONNECT_TIMEOUT); + return (sock->fd != -1); +} + + +/**************************************************************************** + reduce socket reference count - if it becomes zero then close +****************************************************************************/ +void cli_sock_close(struct cli_socket *sock) +{ + sock->reference_count--; + if (sock->reference_count <= 0 && sock->fd != -1) { + close(sock->fd); + sock->fd = -1; + } +} + +/**************************************************************************** + Set socket options on a open connection. +****************************************************************************/ +void cli_sock_set_options(struct cli_socket *sock, const char *options) +{ + set_socket_options(sock->fd, options); +} + +/**************************************************************************** + Write to socket. Return amount written. +****************************************************************************/ +ssize_t cli_sock_write(struct cli_socket *sock, const char *data, size_t len) +{ + return write_data(sock->fd, data, len); +} + + +/**************************************************************************** + Read from socket. return amount read +****************************************************************************/ +ssize_t cli_sock_read(struct cli_socket *sock, char *data, size_t len) +{ + return read_data(sock->fd, data, len); +} + +/**************************************************************************** +resolve a hostname and connect +****************************************************************************/ +BOOL cli_sock_connect_byname(struct cli_socket *sock, const char *host, int port) +{ + int name_type = 0x20; + struct in_addr ip; + TALLOC_CTX *mem_ctx; + char *name, *p; + + if (getenv("LIBSMB_PROG")) { + sock->fd = sock_exec(getenv("LIBSMB_PROG")); + return sock->fd != -1; + } + + mem_ctx = talloc_init("cli_sock_connect_byname"); + if (!mem_ctx) return False; + + name = talloc_strdup(mem_ctx, host); + + /* allow hostnames of the form NAME#xx and do a netbios lookup */ + if ((p = strchr(name, '#'))) { + name_type = strtol(p+1, NULL, 16); + *p = 0; + } + + if (!resolve_name(mem_ctx, name, &ip, name_type)) { + talloc_destroy(mem_ctx); + return False; + } + + talloc_destroy(mem_ctx); + + return cli_sock_connect(sock, &ip, port); +} diff --git a/source4/libcli/raw/clispnego.c b/source4/libcli/raw/clispnego.c new file mode 100644 index 0000000000..53f7eb6e7d --- /dev/null +++ b/source4/libcli/raw/clispnego.c @@ -0,0 +1,533 @@ +/* + Unix SMB/CIFS implementation. + simple kerberos5/SPNEGO routines + Copyright (C) Andrew Tridgell 2001 + Copyright (C) Jim McDonough 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 +*/ +DATA_BLOB spnego_gen_negTokenTarg(const char *principal, int time_offset) +{ + DATA_BLOB tkt, tkt_wrapped, targ; + const char *krb_mechs[] = {OID_KERBEROS5_OLD, OID_NTLMSSP, NULL}; + + /* get a kerberos ticket for the service */ + tkt = krb5_get_ticket(principal, time_offset); + + /* 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 targ; +} + + +/* + 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/source4/libcli/raw/clitransport.c b/source4/libcli/raw/clitransport.c new file mode 100644 index 0000000000..80bb1e301f --- /dev/null +++ b/source4/libcli/raw/clitransport.c @@ -0,0 +1,218 @@ +/* + Unix SMB/CIFS implementation. + SMB client transport context management functions + Copyright (C) Andrew Tridgell 1994-2003 + Copyright (C) James Myers 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 transport structure based on an established socket +*/ +struct cli_transport *cli_transport_init(struct cli_socket *sock) +{ + TALLOC_CTX *mem_ctx; + struct cli_transport *transport; + + mem_ctx = talloc_init("cli_transport"); + if (!mem_ctx) return NULL; + + transport = talloc_zero(mem_ctx, sizeof(*transport)); + if (!transport) return NULL; + + transport->mem_ctx = mem_ctx; + transport->socket = sock; + transport->negotiate.protocol = PROTOCOL_NT1; + transport->negotiate.max_xmit = ~0; + cli_null_set_signing(transport); + transport->socket->reference_count++; + + return transport; +} + +/* + decrease reference count on a transport, and destroy if it becomes + zero +*/ +void cli_transport_close(struct cli_transport *transport) +{ + transport->reference_count--; + if (transport->reference_count <= 0) { + cli_sock_close(transport->socket); + talloc_destroy(transport->mem_ctx); + } +} + + + +/**************************************************************************** +send a session request (if appropriate) +****************************************************************************/ +BOOL cli_transport_connect(struct cli_transport *transport, + struct nmb_name *calling, + struct nmb_name *called) +{ + char *p; + int len = NBT_HDR_SIZE; + struct cli_request *req; + + /* 445 doesn't have session request */ + if (transport->socket->port == 445) { + return True; + } + + /* allocate output buffer */ + req = cli_request_setup_nonsmb(transport, NBT_HDR_SIZE + 2*nbt_mangled_name_len()); + + /* put in the destination name */ + p = req->out.buffer + NBT_HDR_SIZE; + name_mangle(called->name, p, called->name_type); + len += name_len(p); + + /* and my name */ + p = req->out.buffer+len; + name_mangle(calling->name, p, calling->name_type); + len += name_len(p); + + _smb_setlen(req->out.buffer,len-4); + SCVAL(req->out.buffer,0,0x81); + + if (!cli_request_send(req) || + !cli_request_receive(req)) { + cli_request_destroy(req); + return False; + } + + if (CVAL(req->in.buffer,0) != 0x82) { + transport->error.etype = ETYPE_NBT; + transport->error.e.nbt_error = CVAL(req->in.buffer,4); + cli_request_destroy(req); + return False; + } + + cli_request_destroy(req); + return True; +} + + +/**************************************************************************** +get next mid in sequence +****************************************************************************/ +uint16 cli_transport_next_mid(struct cli_transport *transport) +{ + uint16 mid; + struct cli_request *req; + + mid = transport->next_mid; + +again: + /* now check to see if this mid is being used by one of the + pending requests. This is quite efficient because the list is + usually very short */ + + /* the zero mid is reserved for requests that don't have a mid */ + if (mid == 0) mid = 1; + + for (req=transport->pending_requests; req; req=req->next) { + if (req->mid == mid) { + mid++; + goto again; + } + } + + transport->next_mid = mid+1; + return mid; +} + +/* + setup the idle handler for a transport +*/ +void cli_transport_idle_handler(struct cli_transport *transport, + void (*idle_func)(struct cli_transport *, void *), + uint_t period, + void *private) +{ + transport->idle.func = idle_func; + transport->idle.private = private; + transport->idle.period = period; +} + + +/* + determine if a packet is pending for receive on a transport +*/ +BOOL cli_transport_pending(struct cli_transport *transport) +{ + return socket_pending(transport->socket->fd); +} + + + +/* + wait for data on a transport, periodically calling a wait function + if one has been defined + return True if a packet is received +*/ +BOOL cli_transport_select(struct cli_transport *transport) +{ + fd_set fds; + int selrtn; + int fd; + struct timeval timeout; + + fd = transport->socket->fd; + + if (fd == -1) { + return False; + } + + do { + uint_t period = 1000; + + FD_ZERO(&fds); + FD_SET(fd,&fds); + + if (transport->idle.func) { + period = transport->idle.period; + } + + timeout.tv_sec = period / 1000; + timeout.tv_usec = 1000*(period%1000); + + selrtn = sys_select_intr(fd+1,&fds,NULL,NULL,&timeout); + + if (selrtn == 1) { + /* the fd is readable */ + return True; + } + + if (selrtn == -1) { + /* sys_select_intr() already handles EINTR, so this + is an error. The socket is probably dead */ + return False; + } + + /* only other possibility is that we timed out - call the idle function + if there is one */ + if (transport->idle.func) { + transport->idle.func(transport, transport->idle.private); + } + } while (selrtn == 0); + + return True; +} diff --git a/source4/libcli/raw/clitree.c b/source4/libcli/raw/clitree.c new file mode 100644 index 0000000000..2a41273913 --- /dev/null +++ b/source4/libcli/raw/clitree.c @@ -0,0 +1,290 @@ +/* + Unix SMB/CIFS implementation. + SMB client tree context management functions + Copyright (C) Andrew Tridgell 1994-1998 + Copyright (C) James Myers 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 SETUP_REQUEST_TREE(cmd, wct, buflen) do { \ + req = cli_request_setup(tree, cmd, wct, buflen); \ + if (!req) return NULL; \ +} while (0) + + +/**************************************************************************** + Initialize the tree context +****************************************************************************/ +struct cli_tree *cli_tree_init(struct cli_session *session) +{ + struct cli_tree *tree; + TALLOC_CTX *mem_ctx = talloc_init("cli_tree"); + if (mem_ctx == NULL) { + return NULL; + } + + tree = talloc_zero(mem_ctx, sizeof(*tree)); + if (!tree) { + talloc_destroy(mem_ctx); + return NULL; + } + + tree->mem_ctx = mem_ctx; + tree->session = session; + tree->session->reference_count++; + + return tree; +} + +/**************************************************************************** +reduce reference count on a tree and destroy if <= 0 +****************************************************************************/ +void cli_tree_close(struct cli_tree *tree) +{ + if (!tree) return; + tree->reference_count--; + if (tree->reference_count <= 0) { + cli_session_close(tree->session); + talloc_destroy(tree->mem_ctx); + } +} + + +/**************************************************************************** + Send a tconX (async send) +****************************************************************************/ +struct cli_request *smb_tree_connect_send(struct cli_tree *tree, union smb_tcon *parms) +{ + struct cli_request *req; + + switch (parms->tcon.level) { + case RAW_TCON_TCON: + SETUP_REQUEST_TREE(SMBtcon, 0, 0); + cli_req_append_ascii4(req, parms->tcon.in.service, STR_ASCII); + cli_req_append_ascii4(req, parms->tcon.in.password,STR_ASCII); + cli_req_append_ascii4(req, parms->tcon.in.dev, STR_ASCII); + break; + + case RAW_TCON_TCONX: + SETUP_REQUEST_TREE(SMBtconX, 4, 0); + SSVAL(req->out.vwv, VWV(0), 0xFF); + SSVAL(req->out.vwv, VWV(1), 0); + SSVAL(req->out.vwv, VWV(2), parms->tconx.in.flags); + SSVAL(req->out.vwv, VWV(3), parms->tconx.in.password.length); + cli_req_append_blob(req, &parms->tconx.in.password); + cli_req_append_string(req, parms->tconx.in.path, STR_TERMINATE | STR_UPPER); + cli_req_append_string(req, parms->tconx.in.device, STR_TERMINATE | STR_ASCII); + break; + } + + if (!cli_request_send(req)) { + cli_request_destroy(req); + return NULL; + } + + return req; +} + +/**************************************************************************** + Send a tconX (async recv) +****************************************************************************/ +NTSTATUS smb_tree_connect_recv(struct cli_request *req, TALLOC_CTX *mem_ctx, union smb_tcon *parms) +{ + char *p; + + if (!cli_request_receive(req) || + cli_request_is_error(req)) { + goto failed; + } + + switch (parms->tcon.level) { + case RAW_TCON_TCON: + CLI_CHECK_WCT(req, 2); + parms->tcon.out.max_xmit = SVAL(req->in.vwv, VWV(0)); + parms->tcon.out.cnum = SVAL(req->in.vwv, VWV(1)); + break; + + case RAW_TCON_TCONX: + ZERO_STRUCT(parms->tconx.out); + CLI_CHECK_MIN_WCT(req, 0); /* this depends on the protocol level */ + parms->tconx.out.cnum = SVAL(req->in.hdr, HDR_TID); + if (req->in.wct >= 4) { + parms->tconx.out.options = SVAL(req->in.vwv, VWV(3)); + } + + /* output is actual service name */ + p = req->in.data; + if (!p) break; + + p += cli_req_pull_string(req, mem_ctx, &parms->tconx.out.dev_type, + p, -1, STR_ASCII | STR_TERMINATE); + p += cli_req_pull_string(req, mem_ctx, &parms->tconx.out.fs_type, + p, -1, STR_TERMINATE); + break; + } + +failed: + return cli_request_destroy(req); +} + +/**************************************************************************** + Send a tconX (sync interface) +****************************************************************************/ +NTSTATUS smb_tree_connect(struct cli_tree *tree, TALLOC_CTX *mem_ctx, union smb_tcon *parms) +{ + struct cli_request *req = smb_tree_connect_send(tree, parms); + return smb_tree_connect_recv(req, mem_ctx, parms); +} + + +/**************************************************************************** + Send a tree disconnect. +****************************************************************************/ +NTSTATUS smb_tree_disconnect(struct cli_tree *tree) +{ + struct cli_request *req; + + if (!tree) return NT_STATUS_OK; + req = cli_request_setup(tree, SMBtdis, 0, 0); + + if (cli_request_send(req)) { + cli_request_receive(req); + } + return cli_request_destroy(req); +} + + +/* + a convenient function to establish a cli_tree from scratch, using reasonable default + parameters +*/ +NTSTATUS cli_tree_full_connection(struct cli_tree **ret_tree, + const char *my_name, + const char *dest_host, int port, + const char *service, const char *service_type, + const char *user, const char *domain, + const char *password) +{ + struct cli_socket *sock; + struct cli_transport *transport; + struct cli_session *session; + struct cli_tree *tree; + NTSTATUS status; + struct nmb_name calling; + struct nmb_name called; + union smb_sesssetup setup; + union smb_tcon tcon; + TALLOC_CTX *mem_ctx; + + *ret_tree = NULL; + + sock = cli_sock_init(); + if (!sock) { + return NT_STATUS_NO_MEMORY; + } + + /* open a TCP socket to the server */ + if (!cli_sock_connect_byname(sock, dest_host, port)) { + DEBUG(2,("Failed to establish socket connection - %s\n", strerror(errno))); + return NT_STATUS_UNSUCCESSFUL; + } + + transport = cli_transport_init(sock); + if (!transport) { + cli_sock_close(sock); + return NT_STATUS_NO_MEMORY; + } + + /* send a NBT session request, if applicable */ + make_nmb_name(&calling, my_name, 0x0); + make_nmb_name(&called, dest_host, 0x20); + + if (!cli_transport_connect(transport, &calling, &called)) { + cli_transport_close(transport); + return NT_STATUS_UNSUCCESSFUL; + } + + + /* negotiate protocol options with the server */ + status = smb_raw_negotiate(transport); + if (!NT_STATUS_IS_OK(status)) { + cli_transport_close(transport); + return status; + } + + session = cli_session_init(transport); + if (!session) { + cli_transport_close(transport); + return NT_STATUS_NO_MEMORY; + } + + /* prepare a session setup to establish a security context */ + setup.generic.level = RAW_SESSSETUP_GENERIC; + setup.generic.in.sesskey = transport->negotiate.sesskey; + setup.generic.in.capabilities = CAP_UNICODE | CAP_STATUS32 | + CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS | + CAP_W2K_SMBS | CAP_LARGE_READX | CAP_LARGE_WRITEX; + setup.generic.in.password = password; + setup.generic.in.user = user; + setup.generic.in.domain = domain; + + mem_ctx = talloc_init("tcon"); + if (!mem_ctx) { + cli_tree_close(tree); + return NT_STATUS_NO_MEMORY; + } + + status = smb_raw_session_setup(session, mem_ctx, &setup); + if (!NT_STATUS_IS_OK(status)) { + cli_session_close(session); + talloc_destroy(mem_ctx); + return status; + } + + session->vuid = setup.generic.out.vuid; + + tree = cli_tree_init(session); + if (!tree) { + cli_session_close(session); + talloc_destroy(mem_ctx); + return NT_STATUS_NO_MEMORY; + } + + /* connect to a share using a tree connect */ + tcon.generic.level = RAW_TCON_TCONX; + tcon.tconx.in.flags = 0; + tcon.tconx.in.password = data_blob(NULL, 0); + tcon.tconx.in.path = service; + tcon.tconx.in.device = service_type; + + status = smb_tree_connect(tree, mem_ctx, &tcon); + if (!NT_STATUS_IS_OK(status)) { + cli_tree_close(tree); + talloc_destroy(mem_ctx); + return status; + } + + tree->tid = tcon.tconx.out.cnum; + tree->device = talloc_strdup(tree->mem_ctx, tcon.tconx.out.dev_type); + tree->fs_type = talloc_strdup(tree->mem_ctx, tcon.tconx.out.fs_type); + + talloc_destroy(mem_ctx); + + *ret_tree = tree; + return NT_STATUS_OK; +} diff --git a/source4/libcli/raw/raweas.c b/source4/libcli/raw/raweas.c new file mode 100644 index 0000000000..ce0368c304 --- /dev/null +++ b/source4/libcli/raw/raweas.c @@ -0,0 +1,147 @@ +/* + Unix SMB/CIFS implementation. + parsing of EA (extended attribute) lists + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +/* + work out how many bytes on the wire a ea list will consume. + This assumes the names are strict ascii, which should be a + reasonable assumption +*/ +uint_t ea_list_size(uint_t num_eas, struct ea_struct *eas) +{ + uint_t total = 4; + int i; + for (i=0;ilength < 6) { + return 0; + } + + ea->flags = CVAL(blob->data, 0); + nlen = CVAL(blob->data, 1); + vlen = SVAL(blob->data, 2); + + if (nlen+1+vlen > blob->length-4) { + return 0; + } + + ea->name.s = talloc_strndup(mem_ctx, blob->data+4, nlen); + ea->name.private_length = nlen; + ea->value = data_blob_talloc(mem_ctx, NULL, vlen+1); + if (!ea->value.data) return 0; + if (vlen) { + memcpy(ea->value.data, blob->data+4+nlen+1, vlen); + } + ea->value.data[vlen] = 0; + ea->value.length--; + + return 4 + nlen+1 + vlen; +} + + +/* + pull a ea_list from a buffer +*/ +NTSTATUS ea_pull_list(const DATA_BLOB *blob, + TALLOC_CTX *mem_ctx, + uint_t *num_eas, struct ea_struct **eas) +{ + int n; + uint32 ea_size, ofs; + + if (blob->length < 4) { + return NT_STATUS_INFO_LENGTH_MISMATCH; + } + + ea_size = IVAL(blob->data, 0); + if (ea_size > blob->length) { + return NT_STATUS_INVALID_PARAMETER; + } + + ofs = 4; + n = 0; + *num_eas = 0; + *eas = NULL; + + while (ofs < ea_size) { + uint_t len; + DATA_BLOB blob2; + + blob2.data = blob->data + ofs; + blob2.length = ea_size - ofs; + + *eas = talloc_realloc(mem_ctx, *eas, sizeof(**eas) * (n+1)); + if (! *eas) return NT_STATUS_NO_MEMORY; + + len = ea_pull_struct(&blob2, mem_ctx, &(*eas)[n]); + if (len == 0) { + return NT_STATUS_INVALID_PARAMETER; + } + + ofs += len; + n++; + } + + *num_eas = n; + + return NT_STATUS_OK; +} + diff --git a/source4/libcli/raw/rawfile.c b/source4/libcli/raw/rawfile.c new file mode 100644 index 0000000000..279dfcf0c1 --- /dev/null +++ b/source4/libcli/raw/rawfile.c @@ -0,0 +1,687 @@ +/* + Unix SMB/CIFS implementation. + client file operations + Copyright (C) Andrew Tridgell 1994-1998 + Copyright (C) Jeremy Allison 2001-2002 + Copyright (C) James Myers 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 SETUP_REQUEST(cmd, wct, buflen) do { \ + req = cli_request_setup(tree, cmd, wct, buflen); \ + if (!req) return NULL; \ +} while (0) + + +/**************************************************************************** + Rename a file - async interface +****************************************************************************/ +struct cli_request *smb_raw_rename_send(struct cli_tree *tree, + struct smb_rename *parms) +{ + struct cli_request *req; + + SETUP_REQUEST(SMBmv, 1, 0); + + SSVAL(req->out.vwv, VWV(0), parms->in.attrib); + + cli_req_append_ascii4(req, parms->in.pattern1, STR_TERMINATE); + cli_req_append_ascii4(req, parms->in.pattern2, STR_TERMINATE); + + if (!cli_request_send(req)) { + cli_request_destroy(req); + return NULL; + } + + return req; +} + +/**************************************************************************** + Rename a file - sync interface +****************************************************************************/ +NTSTATUS smb_raw_rename(struct cli_tree *tree, + struct smb_rename *parms) +{ + struct cli_request *req = smb_raw_rename_send(tree, parms); + return cli_request_simple_recv(req); +} + + +/**************************************************************************** + Delete a file - async interface +****************************************************************************/ +struct cli_request *smb_raw_unlink_send(struct cli_tree *tree, + struct smb_unlink *parms) +{ + struct cli_request *req; + + SETUP_REQUEST(SMBunlink, 1, 0); + + SSVAL(req->out.vwv, VWV(0), parms->in.attrib); + cli_req_append_ascii4(req, parms->in.pattern, STR_TERMINATE); + + if (!cli_request_send(req)) { + cli_request_destroy(req); + return NULL; + } + return req; +} + +/* + delete a file - sync interface +*/ +NTSTATUS smb_raw_unlink(struct cli_tree *tree, + struct smb_unlink *parms) +{ + struct cli_request *req = smb_raw_unlink_send(tree, parms); + return cli_request_simple_recv(req); +} + + +/**************************************************************************** + create a directory using TRANSACT2_MKDIR - async interface +****************************************************************************/ +static struct cli_request *smb_raw_t2mkdir_send(struct cli_tree *tree, + union smb_mkdir *parms) +{ + struct smb_trans2 t2; + uint16 setup = TRANSACT2_MKDIR; + TALLOC_CTX *mem_ctx; + struct cli_request *req; + uint16 data_total; + + mem_ctx = talloc_init("t2mkdir"); + + data_total = ea_list_size(parms->t2mkdir.in.num_eas, parms->t2mkdir.in.eas); + + t2.in.max_param = 0; + t2.in.max_data = 0; + t2.in.max_setup = 0; + t2.in.flags = 0; + t2.in.timeout = 0; + t2.in.setup_count = 1; + t2.in.setup = &setup; + t2.in.params = data_blob_talloc(mem_ctx, NULL, 4); + t2.in.data = data_blob_talloc(mem_ctx, NULL, data_total); + + SIVAL(t2.in.params.data, VWV(0), 0); /* reserved */ + + cli_blob_append_string(tree->session, mem_ctx, + &t2.in.params, parms->t2mkdir.in.path, 0); + + ea_put_list(t2.in.data.data, parms->t2mkdir.in.num_eas, parms->t2mkdir.in.eas); + + req = smb_raw_trans2_send(tree, &t2); + + talloc_destroy(mem_ctx); + + return req; +} + +/**************************************************************************** + Create a directory - async interface +****************************************************************************/ +struct cli_request *smb_raw_mkdir_send(struct cli_tree *tree, + union smb_mkdir *parms) +{ + struct cli_request *req; + + if (parms->generic.level == RAW_MKDIR_T2MKDIR) { + return smb_raw_t2mkdir_send(tree, parms); + } + + if (parms->generic.level != RAW_MKDIR_MKDIR) { + return NULL; + } + + SETUP_REQUEST(SMBmkdir, 0, 0); + + cli_req_append_ascii4(req, parms->mkdir.in.path, STR_TERMINATE); + + if (!cli_request_send(req)) { + return NULL; + } + + return req; +} + +/**************************************************************************** + Create a directory - sync interface +****************************************************************************/ +NTSTATUS smb_raw_mkdir(struct cli_tree *tree, + union smb_mkdir *parms) +{ + struct cli_request *req = smb_raw_mkdir_send(tree, parms); + return cli_request_simple_recv(req); +} + +/**************************************************************************** + Remove a directory - async interface +****************************************************************************/ +struct cli_request *smb_raw_rmdir_send(struct cli_tree *tree, + struct smb_rmdir *parms) +{ + struct cli_request *req; + + SETUP_REQUEST(SMBrmdir, 0, 0); + + cli_req_append_ascii4(req, parms->in.path, STR_TERMINATE); + + if (!cli_request_send(req)) { + cli_request_destroy(req); + return NULL; + } + + return req; +} + +/**************************************************************************** + Remove a directory - sync interface +****************************************************************************/ +NTSTATUS smb_raw_rmdir(struct cli_tree *tree, + struct smb_rmdir *parms) +{ + struct cli_request *req = smb_raw_rmdir_send(tree, parms); + return cli_request_simple_recv(req); +} + + +/**************************************************************************** + Open a file using TRANSACT2_OPEN - async send +****************************************************************************/ +static struct cli_request *smb_raw_t2open_send(struct cli_tree *tree, + union smb_open *parms) +{ + struct smb_trans2 t2; + uint16 setup = TRANSACT2_OPEN; + TALLOC_CTX *mem_ctx = talloc_init("smb_raw_t2open"); + struct cli_request *req; + uint16 list_size; + + list_size = ea_list_size(parms->t2open.in.num_eas, parms->t2open.in.eas); + + t2.in.max_param = 30; + t2.in.max_data = 0; + t2.in.max_setup = 0; + t2.in.flags = 0; + t2.in.timeout = 0; + t2.in.setup_count = 1; + t2.in.setup = &setup; + t2.in.params = data_blob_talloc(mem_ctx, NULL, 28); + t2.in.data = data_blob_talloc(mem_ctx, NULL, list_size); + + SSVAL(t2.in.params.data, VWV(0), parms->t2open.in.flags); + SSVAL(t2.in.params.data, VWV(1), parms->t2open.in.open_mode); + SSVAL(t2.in.params.data, VWV(2), 0); /* reserved */ + SSVAL(t2.in.params.data, VWV(3), parms->t2open.in.file_attrs); + put_dos_date(t2.in.params.data, VWV(4), parms->t2open.in.write_time); + SSVAL(t2.in.params.data, VWV(6), parms->t2open.in.open_func); + SIVAL(t2.in.params.data, VWV(7), parms->t2open.in.size); + SIVAL(t2.in.params.data, VWV(9), parms->t2open.in.timeout); + SIVAL(t2.in.params.data, VWV(11), 0); + SSVAL(t2.in.params.data, VWV(13), 0); + + cli_blob_append_string(tree->session, mem_ctx, + &t2.in.params, parms->t2open.in.fname, + STR_TERMINATE); + + ea_put_list(t2.in.data.data, parms->t2open.in.num_eas, parms->t2open.in.eas); + + req = smb_raw_trans2_send(tree, &t2); + + talloc_destroy(mem_ctx); + + return req; +} + + +/**************************************************************************** + Open a file using TRANSACT2_OPEN - async recv +****************************************************************************/ +static NTSTATUS smb_raw_t2open_recv(struct cli_request *req, TALLOC_CTX *mem_ctx, union smb_open *parms) +{ + struct smb_trans2 t2; + NTSTATUS status; + + status = smb_raw_trans2_recv(req, mem_ctx, &t2); + if (!NT_STATUS_IS_OK(status)) return status; + + if (t2.out.params.length < 30) { + return NT_STATUS_INFO_LENGTH_MISMATCH; + } + + parms->t2open.out.fnum = SVAL(t2.out.params.data, VWV(0)); + parms->t2open.out.attrib = SVAL(t2.out.params.data, VWV(1)); + parms->t2open.out.write_time = make_unix_date3(t2.out.params.data + VWV(2)); + parms->t2open.out.size = IVAL(t2.out.params.data, VWV(4)); + parms->t2open.out.access = SVAL(t2.out.params.data, VWV(6)); + parms->t2open.out.ftype = SVAL(t2.out.params.data, VWV(7)); + parms->t2open.out.devstate = SVAL(t2.out.params.data, VWV(8)); + parms->t2open.out.action = SVAL(t2.out.params.data, VWV(9)); + parms->t2open.out.unknown = SVAL(t2.out.params.data, VWV(10)); + + return NT_STATUS_OK; +} + +/**************************************************************************** + Open a file - async send +****************************************************************************/ +struct cli_request *smb_raw_open_send(struct cli_tree *tree, union smb_open *parms) +{ + int len; + struct cli_request *req = NULL; + + switch (parms->open.level) { + case RAW_OPEN_T2OPEN: + return smb_raw_t2open_send(tree, parms); + + case RAW_OPEN_OPEN: + SETUP_REQUEST(SMBopen, 2, 0); + SSVAL(req->out.vwv, VWV(0), parms->open.in.flags); + SSVAL(req->out.vwv, VWV(1), parms->open.in.search_attrs); + cli_req_append_ascii4(req, parms->open.in.fname, STR_TERMINATE); + break; + + case RAW_OPEN_OPENX: + SETUP_REQUEST(SMBopenX, 15, 0); + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + SSVAL(req->out.vwv, VWV(2), parms->openx.in.flags); + SSVAL(req->out.vwv, VWV(3), parms->openx.in.open_mode); + SSVAL(req->out.vwv, VWV(4), parms->openx.in.search_attrs); + SSVAL(req->out.vwv, VWV(5), parms->openx.in.file_attrs); + put_dos_date3(req->out.vwv, VWV(6), parms->openx.in.write_time); + SSVAL(req->out.vwv, VWV(8), parms->openx.in.open_func); + SIVAL(req->out.vwv, VWV(9), parms->openx.in.size); + SIVAL(req->out.vwv, VWV(11),parms->openx.in.timeout); + SIVAL(req->out.vwv, VWV(13),0); /* reserved */ + cli_req_append_string(req, parms->openx.in.fname, STR_TERMINATE); + break; + + case RAW_OPEN_MKNEW: + SETUP_REQUEST(SMBmknew, 3, 0); + SSVAL(req->out.vwv, VWV(0), parms->mknew.in.attrib); + put_dos_date3(req->out.vwv, VWV(1), parms->mknew.in.write_time); + cli_req_append_ascii4(req, parms->mknew.in.fname, STR_TERMINATE); + break; + + case RAW_OPEN_CTEMP: + SETUP_REQUEST(SMBctemp, 3, 0); + SSVAL(req->out.vwv, VWV(0), parms->ctemp.in.attrib); + put_dos_date3(req->out.vwv, VWV(1), parms->ctemp.in.write_time); + cli_req_append_ascii4(req, parms->ctemp.in.directory, STR_TERMINATE); + break; + + case RAW_OPEN_SPLOPEN: + SETUP_REQUEST(SMBsplopen, 2, 0); + SSVAL(req->out.vwv, VWV(0), parms->splopen.in.setup_length); + SSVAL(req->out.vwv, VWV(1), parms->splopen.in.mode); + break; + + case RAW_OPEN_NTCREATEX: + SETUP_REQUEST(SMBntcreateX, 24, 0); + SSVAL(req->out.vwv, VWV(0),SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1),0); + SCVAL(req->out.vwv, VWV(2),0); /* padding */ + SIVAL(req->out.vwv, 7, parms->ntcreatex.in.flags); + SIVAL(req->out.vwv, 11, parms->ntcreatex.in.root_fid); + SIVAL(req->out.vwv, 15, parms->ntcreatex.in.access_mask); + SBVAL(req->out.vwv, 19, parms->ntcreatex.in.alloc_size); + SIVAL(req->out.vwv, 27, parms->ntcreatex.in.file_attr); + SIVAL(req->out.vwv, 31, parms->ntcreatex.in.share_access); + SIVAL(req->out.vwv, 35, parms->ntcreatex.in.open_disposition); + SIVAL(req->out.vwv, 39, parms->ntcreatex.in.create_options); + SIVAL(req->out.vwv, 43, parms->ntcreatex.in.impersonation); + SCVAL(req->out.vwv, 47, parms->ntcreatex.in.security_flags); + + cli_req_append_string_len(req, parms->ntcreatex.in.fname, STR_TERMINATE, &len); + SSVAL(req->out.vwv, 5, len); + break; + } + + if (!cli_request_send(req)) { + cli_request_destroy(req); + return NULL; + } + + return req; +} + +/**************************************************************************** + Open a file - async recv +****************************************************************************/ +NTSTATUS smb_raw_open_recv(struct cli_request *req, TALLOC_CTX *mem_ctx, union smb_open *parms) +{ + if (!cli_request_receive(req) || + cli_request_is_error(req)) { + goto failed; + } + + switch (parms->open.level) { + case RAW_OPEN_T2OPEN: + return smb_raw_t2open_recv(req, mem_ctx, parms); + + case RAW_OPEN_OPEN: + CLI_CHECK_WCT(req, 7); + parms->open.out.fnum = SVAL(req->in.vwv, VWV(0)); + parms->open.out.attrib = SVAL(req->in.vwv, VWV(1)); + parms->open.out.write_time = make_unix_date3(req->in.vwv + VWV(2)); + parms->open.out.size = IVAL(req->in.vwv, VWV(4)); + parms->open.out.rmode = SVAL(req->in.vwv, VWV(6)); + break; + + case RAW_OPEN_OPENX: + CLI_CHECK_MIN_WCT(req, 15); + parms->openx.out.fnum = SVAL(req->in.vwv, VWV(2)); + parms->openx.out.attrib = SVAL(req->in.vwv, VWV(3)); + parms->openx.out.write_time = make_unix_date3(req->in.vwv + VWV(4)); + parms->openx.out.size = IVAL(req->in.vwv, VWV(6)); + parms->openx.out.access = SVAL(req->in.vwv, VWV(8)); + parms->openx.out.ftype = SVAL(req->in.vwv, VWV(9)); + parms->openx.out.devstate = SVAL(req->in.vwv, VWV(10)); + parms->openx.out.action = SVAL(req->in.vwv, VWV(11)); + parms->openx.out.unique_fid = IVAL(req->in.vwv, VWV(12)); + if (req->in.wct >= 19) { + parms->openx.out.access_mask = IVAL(req->in.vwv, VWV(15)); + parms->openx.out.unknown = IVAL(req->in.vwv, VWV(17)); + } else { + parms->openx.out.access_mask = 0; + parms->openx.out.unknown = 0; + } + break; + + case RAW_OPEN_MKNEW: + CLI_CHECK_WCT(req, 1); + parms->mknew.out.fnum = SVAL(req->in.vwv, VWV(0)); + break; + + case RAW_OPEN_CTEMP: + CLI_CHECK_WCT(req, 1); + parms->ctemp.out.fnum = SVAL(req->in.vwv, VWV(0)); + cli_req_pull_string(req, mem_ctx, &parms->ctemp.out.name, req->in.data, -1, STR_TERMINATE | STR_ASCII); + break; + + case RAW_OPEN_SPLOPEN: + CLI_CHECK_WCT(req, 1); + parms->splopen.out.fnum = SVAL(req->in.vwv, VWV(0)); + break; + + case RAW_OPEN_NTCREATEX: + CLI_CHECK_MIN_WCT(req, 34); + parms->ntcreatex.out.oplock_level = CVAL(req->in.vwv, 4); + parms->ntcreatex.out.fnum = SVAL(req->in.vwv, 5); + parms->ntcreatex.out.create_action = IVAL(req->in.vwv, 7); + parms->ntcreatex.out.create_time = cli_pull_nttime(req->in.vwv, 11); + parms->ntcreatex.out.access_time = cli_pull_nttime(req->in.vwv, 19); + parms->ntcreatex.out.write_time = cli_pull_nttime(req->in.vwv, 27); + parms->ntcreatex.out.change_time = cli_pull_nttime(req->in.vwv, 35); + parms->ntcreatex.out.attrib = IVAL(req->in.vwv, 43); + parms->ntcreatex.out.alloc_size = BVAL(req->in.vwv, 47); + parms->ntcreatex.out.size = BVAL(req->in.vwv, 55); + parms->ntcreatex.out.file_type = SVAL(req->in.vwv, 63); + parms->ntcreatex.out.ipc_state = SVAL(req->in.vwv, 65); + parms->ntcreatex.out.is_directory = CVAL(req->in.vwv, 67); + break; + } + +failed: + return cli_request_destroy(req); +} + + +/**************************************************************************** + Open a file - sync interface +****************************************************************************/ +NTSTATUS smb_raw_open(struct cli_tree *tree, TALLOC_CTX *mem_ctx, union smb_open *parms) +{ + struct cli_request *req = smb_raw_open_send(tree, parms); + return smb_raw_open_recv(req, mem_ctx, parms); +} + + +/**************************************************************************** + Close a file - async send +****************************************************************************/ +struct cli_request *smb_raw_close_send(struct cli_tree *tree, union smb_close *parms) +{ + struct cli_request *req; + + switch (parms->generic.level) { + case RAW_CLOSE_GENERIC: + return NULL; + + case RAW_CLOSE_CLOSE: + SETUP_REQUEST(SMBclose, 3, 0); + SSVAL(req->out.vwv, VWV(0), parms->close.in.fnum); + put_dos_date3(req->out.vwv, VWV(1), parms->close.in.write_time); + break; + + case RAW_CLOSE_SPLCLOSE: + SETUP_REQUEST(SMBsplclose, 3, 0); + SSVAL(req->out.vwv, VWV(0), parms->splclose.in.fnum); + SIVAL(req->out.vwv, VWV(1), 0); /* reserved */ + break; + } + + if (!req) return NULL; + + if (!cli_request_send(req)) { + cli_request_destroy(req); + return NULL; + } + + return req; +} + + +/**************************************************************************** + Close a file - sync interface +****************************************************************************/ +NTSTATUS smb_raw_close(struct cli_tree *tree, union smb_close *parms) +{ + struct cli_request *req = smb_raw_close_send(tree, parms); + return cli_request_simple_recv(req); +} + + +/**************************************************************************** + Locking calls - async interface +****************************************************************************/ +struct cli_request *smb_raw_lock_send(struct cli_tree *tree, union smb_lock *parms) +{ + struct cli_request *req; + + switch (parms->generic.level) { + case RAW_LOCK_GENERIC: + return NULL; + + case RAW_LOCK_LOCK: + SETUP_REQUEST(SMBlock, 5, 0); + SSVAL(req->out.vwv, VWV(0), parms->lock.in.fnum); + SIVAL(req->out.vwv, VWV(1), parms->lock.in.count); + SIVAL(req->out.vwv, VWV(3), parms->lock.in.offset); + break; + + case RAW_LOCK_UNLOCK: + SETUP_REQUEST(SMBunlock, 5, 0); + SSVAL(req->out.vwv, VWV(0), parms->unlock.in.fnum); + SIVAL(req->out.vwv, VWV(1), parms->unlock.in.count); + SIVAL(req->out.vwv, VWV(3), parms->unlock.in.offset); + break; + + case RAW_LOCK_LOCKX: { + struct smb_lock_entry *lockp; + uint_t lck_size = (parms->lockx.in.mode & LOCKING_ANDX_LARGE_FILES)? 20 : 10; + uint_t lock_count = parms->lockx.in.ulock_cnt + parms->lockx.in.lock_cnt; + int i; + + SETUP_REQUEST(SMBlockingX, 8, lck_size * lock_count); + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + SSVAL(req->out.vwv, VWV(2), parms->lockx.in.fnum); + SSVAL(req->out.vwv, VWV(3), parms->lockx.in.mode); + SIVAL(req->out.vwv, VWV(4), parms->lockx.in.timeout); + SSVAL(req->out.vwv, VWV(6), parms->lockx.in.ulock_cnt); + SSVAL(req->out.vwv, VWV(7), parms->lockx.in.lock_cnt); + + /* copy in all the locks */ + lockp = &parms->lockx.in.locks[0]; + for (i = 0; i < lock_count; i++) { + char *p = req->out.data + lck_size * i; + SSVAL(p, 0, lockp[i].pid); + if (parms->lockx.in.mode & LOCKING_ANDX_LARGE_FILES) { + SSVAL(p, 2, 0); /* reserved */ + SIVAL(p, 4, lockp[i].offset>>32); + SIVAL(p, 8, lockp[i].offset); + SIVAL(p, 12, lockp[i].count>>32); + SIVAL(p, 16, lockp[i].count); + } else { + SIVAL(p, 2, lockp[i].offset); + SIVAL(p, 6, lockp[i].count); + } + } + } + } + + if (!cli_request_send(req)) { + cli_request_destroy(req); + return NULL; + } + + return req; +} + +/**************************************************************************** + Locking calls - sync interface +****************************************************************************/ +NTSTATUS smb_raw_lock(struct cli_tree *tree, union smb_lock *parms) +{ + struct cli_request *req = smb_raw_lock_send(tree, parms); + return cli_request_simple_recv(req); +} + + +/**************************************************************************** + Check for existence of a dir - async send +****************************************************************************/ +struct cli_request *smb_raw_chkpath_send(struct cli_tree *tree, struct smb_chkpath *parms) +{ + struct cli_request *req; + + SETUP_REQUEST(SMBchkpth, 0, 0); + + cli_req_append_ascii4(req, parms->in.path, STR_TERMINATE); + + if (!cli_request_send(req)) { + cli_request_destroy(req); + return NULL; + } + + return req; +} + +/**************************************************************************** + Check for existence of a dir - sync interface +****************************************************************************/ +NTSTATUS smb_raw_chkpath(struct cli_tree *tree, struct smb_chkpath *parms) +{ + struct cli_request *req = smb_raw_chkpath_send(tree, parms); + return cli_request_simple_recv(req); +} + + + + +/**************************************************************************** + flush a file - async send + a flush to fnum 0xFFFF will flush all files +****************************************************************************/ +struct cli_request *smb_raw_flush_send(struct cli_tree *tree, struct smb_flush *parms) +{ + struct cli_request *req; + + SETUP_REQUEST(SMBflush, 1, 0); + SSVAL(req->out.vwv, VWV(0), parms->in.fnum); + + if (!cli_request_send(req)) { + cli_request_destroy(req); + return NULL; + } + + return req; +} + + +/**************************************************************************** + flush a file - sync interface +****************************************************************************/ +NTSTATUS smb_raw_flush(struct cli_tree *tree, struct smb_flush *parms) +{ + struct cli_request *req = smb_raw_flush_send(tree, parms); + return cli_request_simple_recv(req); +} + + +/**************************************************************************** + seek a file - async send +****************************************************************************/ +struct cli_request *smb_raw_seek_send(struct cli_tree *tree, + struct smb_seek *parms) +{ + struct cli_request *req; + + SETUP_REQUEST(SMBlseek, 4, 0); + + SSVAL(req->out.vwv, VWV(0), parms->in.fnum); + SSVAL(req->out.vwv, VWV(1), parms->in.mode); + SIVALS(req->out.vwv, VWV(2), parms->in.offset); + + if (!cli_request_send(req)) { + cli_request_destroy(req); + return NULL; + } + return req; +} + +/**************************************************************************** + seek a file - async receive +****************************************************************************/ +NTSTATUS smb_raw_seek_recv(struct cli_request *req, + struct smb_seek *parms) +{ + if (!cli_request_receive(req) || + cli_request_is_error(req)) { + return cli_request_destroy(req); + } + + CLI_CHECK_WCT(req, 2); + parms->out.offset = IVAL(req->in.vwv, VWV(0)); + +failed: + return cli_request_destroy(req); +} + +/* + seek a file - sync interface +*/ +NTSTATUS smb_raw_seek(struct cli_tree *tree, + struct smb_seek *parms) +{ + struct cli_request *req = smb_raw_seek_send(tree, parms); + return smb_raw_seek_recv(req, parms); +} diff --git a/source4/libcli/raw/rawfileinfo.c b/source4/libcli/raw/rawfileinfo.c new file mode 100644 index 0000000000..f685cef9c3 --- /dev/null +++ b/source4/libcli/raw/rawfileinfo.c @@ -0,0 +1,527 @@ +/* + Unix SMB/CIFS implementation. + client trans2 operations + Copyright (C) James Myers 2003 + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +/* local macros to make the code more readable */ +#define FINFO_CHECK_MIN_SIZE(size) if (blob->length < (size)) { \ + DEBUG(1,("Unexpected FILEINFO reply size %d for level %u - expected min of %d\n", \ + blob->length, parms->generic.level, (size))); \ + return NT_STATUS_INFO_LENGTH_MISMATCH; \ +} +#define FINFO_CHECK_SIZE(size) if (blob->length != (size)) { \ + DEBUG(1,("Unexpected FILEINFO reply size %d for level %u - expected %d\n", \ + blob->length, parms->generic.level, (size))); \ + return NT_STATUS_INFO_LENGTH_MISMATCH; \ +} + +/**************************************************************************** + Handle qfileinfo/qpathinfo trans2 backend. +****************************************************************************/ +static NTSTATUS smb_raw_info_backend(struct cli_session *session, + TALLOC_CTX *mem_ctx, + union smb_fileinfo *parms, + DATA_BLOB *blob) +{ + uint_t len, ofs; + + switch (parms->generic.level) { + case RAW_FILEINFO_GENERIC: + case RAW_FILEINFO_GETATTR: + case RAW_FILEINFO_GETATTRE: + /* not handled here */ + return NT_STATUS_INVALID_LEVEL; + + case RAW_FILEINFO_STANDARD: + FINFO_CHECK_SIZE(22); + parms->standard.out.create_time = make_unix_date2(blob->data + 0); + parms->standard.out.access_time = make_unix_date2(blob->data + 4); + parms->standard.out.write_time = make_unix_date2(blob->data + 8); + parms->standard.out.size = IVAL(blob->data, 12); + parms->standard.out.alloc_size = IVAL(blob->data, 16); + parms->standard.out.attrib = SVAL(blob->data, 20); + return NT_STATUS_OK; + + case RAW_FILEINFO_EA_SIZE: + FINFO_CHECK_SIZE(26); + parms->ea_size.out.create_time = make_unix_date2(blob->data + 0); + parms->ea_size.out.access_time = make_unix_date2(blob->data + 4); + parms->ea_size.out.write_time = make_unix_date2(blob->data + 8); + parms->ea_size.out.size = IVAL(blob->data, 12); + parms->ea_size.out.alloc_size = IVAL(blob->data, 16); + parms->ea_size.out.attrib = SVAL(blob->data, 20); + parms->ea_size.out.ea_size = IVAL(blob->data, 22); + return NT_STATUS_OK; + + case RAW_FILEINFO_ALL_EAS: + FINFO_CHECK_MIN_SIZE(4); + return ea_pull_list(blob, mem_ctx, + &parms->all_eas.out.num_eas, + &parms->all_eas.out.eas); + + case RAW_FILEINFO_IS_NAME_VALID: + /* no data! */ + FINFO_CHECK_SIZE(0); + return NT_STATUS_OK; + + case RAW_FILEINFO_BASIC_INFO: + case RAW_FILEINFO_BASIC_INFORMATION: + /* some servers return 40 bytes and some 36. w2k3 return 40, so thats + what we should do, but we need to accept 36 */ + if (blob->length != 36) { + FINFO_CHECK_SIZE(40); + } + parms->basic_info.out.create_time = cli_pull_nttime(blob->data, 0); + parms->basic_info.out.access_time = cli_pull_nttime(blob->data, 8); + parms->basic_info.out.write_time = cli_pull_nttime(blob->data, 16); + parms->basic_info.out.change_time = cli_pull_nttime(blob->data, 24); + parms->basic_info.out.attrib = IVAL(blob->data, 32); + return NT_STATUS_OK; + + case RAW_FILEINFO_STANDARD_INFO: + case RAW_FILEINFO_STANDARD_INFORMATION: + FINFO_CHECK_SIZE(24); + parms->standard_info.out.alloc_size = BVAL(blob->data, 0); + parms->standard_info.out.size = BVAL(blob->data, 8); + parms->standard_info.out.nlink = IVAL(blob->data, 16); + parms->standard_info.out.delete_pending = CVAL(blob->data, 20); + parms->standard_info.out.directory = CVAL(blob->data, 21); + return NT_STATUS_OK; + + case RAW_FILEINFO_EA_INFO: + case RAW_FILEINFO_EA_INFORMATION: + FINFO_CHECK_SIZE(4); + parms->ea_info.out.ea_size = IVAL(blob->data, 0); + return NT_STATUS_OK; + + case RAW_FILEINFO_NAME_INFO: + case RAW_FILEINFO_NAME_INFORMATION: + FINFO_CHECK_MIN_SIZE(4); + cli_blob_pull_string(session, mem_ctx, blob, + &parms->name_info.out.fname, 0, 4, STR_UNICODE); + return NT_STATUS_OK; + + case RAW_FILEINFO_ALL_INFO: + case RAW_FILEINFO_ALL_INFORMATION: + FINFO_CHECK_MIN_SIZE(72); + parms->all_info.out.create_time = cli_pull_nttime(blob->data, 0); + parms->all_info.out.access_time = cli_pull_nttime(blob->data, 8); + parms->all_info.out.write_time = cli_pull_nttime(blob->data, 16); + parms->all_info.out.change_time = cli_pull_nttime(blob->data, 24); + parms->all_info.out.attrib = IVAL(blob->data, 32); + parms->all_info.out.alloc_size = BVAL(blob->data, 40); + parms->all_info.out.size = BVAL(blob->data, 48); + parms->all_info.out.nlink = IVAL(blob->data, 56); + parms->all_info.out.delete_pending = CVAL(blob->data, 60); + parms->all_info.out.directory = CVAL(blob->data, 61); + parms->all_info.out.ea_size = IVAL(blob->data, 64); + cli_blob_pull_string(session, mem_ctx, blob, + &parms->all_info.out.fname, 68, 72, STR_UNICODE); + return NT_STATUS_OK; + + case RAW_FILEINFO_ALT_NAME_INFO: + case RAW_FILEINFO_ALT_NAME_INFORMATION: + FINFO_CHECK_MIN_SIZE(4); + cli_blob_pull_string(session, mem_ctx, blob, + &parms->alt_name_info.out.fname, 0, 4, STR_UNICODE); + return NT_STATUS_OK; + + case RAW_FILEINFO_STREAM_INFO: + case RAW_FILEINFO_STREAM_INFORMATION: + FINFO_CHECK_MIN_SIZE(0); + ofs = 0; + parms->stream_info.out.num_streams = 0; + parms->stream_info.out.streams = NULL; + + while (blob->length - ofs >= 24) { + uint_t n = parms->stream_info.out.num_streams; + parms->stream_info.out.streams = + talloc_realloc(mem_ctx,parms->stream_info.out.streams, + (n+1) * sizeof(parms->stream_info.out.streams[0])); + if (!parms->stream_info.out.streams) { + return NT_STATUS_NO_MEMORY; + } + parms->stream_info.out.streams[n].size = BVAL(blob->data, ofs + 8); + parms->stream_info.out.streams[n].alloc_size = BVAL(blob->data, ofs + 16); + cli_blob_pull_string(session, mem_ctx, blob, + &parms->stream_info.out.streams[n].stream_name, + ofs+4, ofs+24, STR_UNICODE); + parms->stream_info.out.num_streams++; + len = IVAL(blob->data, ofs); + if (len > blob->length - ofs) return NT_STATUS_INFO_LENGTH_MISMATCH; + if (len == 0) break; + ofs += len; + } + return NT_STATUS_OK; + + case RAW_FILEINFO_INTERNAL_INFORMATION: + FINFO_CHECK_SIZE(8); + parms->internal_information.out.device = IVAL(blob->data, 0); + parms->internal_information.out.inode = IVAL(blob->data, 4); + return NT_STATUS_OK; + + case RAW_FILEINFO_ACCESS_INFORMATION: + FINFO_CHECK_SIZE(4); + parms->access_information.out.access_flags = IVAL(blob->data, 0); + return NT_STATUS_OK; + + case RAW_FILEINFO_POSITION_INFORMATION: + FINFO_CHECK_SIZE(8); + parms->position_information.out.position = BVAL(blob->data, 0); + return NT_STATUS_OK; + + case RAW_FILEINFO_MODE_INFORMATION: + FINFO_CHECK_SIZE(4); + parms->mode_information.out.mode = IVAL(blob->data, 0); + return NT_STATUS_OK; + + case RAW_FILEINFO_ALIGNMENT_INFORMATION: + FINFO_CHECK_SIZE(4); + parms->alignment_information.out.alignment_requirement + = IVAL(blob->data, 0); + return NT_STATUS_OK; + + case RAW_FILEINFO_COMPRESSION_INFO: + case RAW_FILEINFO_COMPRESSION_INFORMATION: + FINFO_CHECK_SIZE(16); + parms->compression_info.out.compressed_size = BVAL(blob->data, 0); + parms->compression_info.out.format = SVAL(blob->data, 8); + parms->compression_info.out.unit_shift = CVAL(blob->data, 10); + parms->compression_info.out.chunk_shift = CVAL(blob->data, 11); + parms->compression_info.out.cluster_shift = CVAL(blob->data, 12); + /* 3 bytes of padding */ + return NT_STATUS_OK; + + case RAW_FILEINFO_UNIX_BASIC: + FINFO_CHECK_SIZE(100); + parms->unix_basic_info.out.end_of_file = BVAL(blob->data, 0); + parms->unix_basic_info.out.num_bytes = BVAL(blob->data, 8); + parms->unix_basic_info.out.status_change_time = cli_pull_nttime(blob->data, 16); + parms->unix_basic_info.out.access_time = cli_pull_nttime(blob->data, 24); + parms->unix_basic_info.out.change_time = cli_pull_nttime(blob->data, 32); + parms->unix_basic_info.out.uid = BVAL(blob->data, 40); + parms->unix_basic_info.out.gid = BVAL(blob->data, 48); + parms->unix_basic_info.out.file_type = IVAL(blob->data, 52); + parms->unix_basic_info.out.dev_major = BVAL(blob->data, 60); + parms->unix_basic_info.out.dev_minor = BVAL(blob->data, 68); + parms->unix_basic_info.out.unique_id = BVAL(blob->data, 76); + parms->unix_basic_info.out.permissions = BVAL(blob->data, 84); + parms->unix_basic_info.out.nlink = BVAL(blob->data, 92); + return NT_STATUS_OK; + + case RAW_FILEINFO_UNIX_LINK: + FINFO_CHECK_MIN_SIZE(0); + cli_blob_pull_string(session, mem_ctx, blob, + &parms->unix_link_info.out.link_dest, 0, 4, STR_UNICODE); + return NT_STATUS_OK; + + case RAW_FILEINFO_NETWORK_OPEN_INFORMATION: + FINFO_CHECK_SIZE(56); + parms->network_open_information.out.create_time = cli_pull_nttime(blob->data, 0); + parms->network_open_information.out.access_time = cli_pull_nttime(blob->data, 8); + parms->network_open_information.out.write_time = cli_pull_nttime(blob->data, 16); + parms->network_open_information.out.change_time = cli_pull_nttime(blob->data, 24); + parms->network_open_information.out.alloc_size = BVAL(blob->data, 32); + parms->network_open_information.out.size = BVAL(blob->data, 40); + parms->network_open_information.out.attrib = IVAL(blob->data, 48); + return NT_STATUS_OK; + + case RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION: + FINFO_CHECK_SIZE(8); + parms->attribute_tag_information.out.attrib = IVAL(blob->data, 0); + parms->attribute_tag_information.out.reparse_tag = IVAL(blob->data, 4); + return NT_STATUS_OK; + } + + return NT_STATUS_INVALID_LEVEL; +} + +/**************************************************************************** + Very raw query file info - returns param/data blobs - (async send) +****************************************************************************/ +static struct cli_request *smb_raw_fileinfo_blob_send(struct cli_tree *tree, + uint16 fnum, uint16 info_level) +{ + struct smb_trans2 tp; + uint16 setup = TRANSACT2_QFILEINFO; + struct cli_request *req; + TALLOC_CTX *mem_ctx = talloc_init("raw_fileinfo"); + + tp.in.max_setup = 0; + tp.in.flags = 0; + tp.in.timeout = 0; + tp.in.setup_count = 1; + tp.in.data = data_blob(NULL, 0); + tp.in.max_param = 2; + tp.in.max_data = 0xFFFF; + tp.in.setup = &setup; + + tp.in.params = data_blob_talloc(mem_ctx, NULL, 4); + if (!tp.in.params.data) { + talloc_destroy(mem_ctx); + return NULL; + } + + SIVAL(tp.in.params.data, 0, fnum); + SSVAL(tp.in.params.data, 2, info_level); + + req = smb_raw_trans2_send(tree, &tp); + + talloc_destroy(mem_ctx); + + return req; +} + + +/**************************************************************************** + Very raw query file info - returns param/data blobs - (async recv) +****************************************************************************/ +static NTSTATUS smb_raw_fileinfo_blob_recv(struct cli_request *req, + TALLOC_CTX *mem_ctx, + DATA_BLOB *blob) +{ + struct smb_trans2 tp; + NTSTATUS status = smb_raw_trans2_recv(req, mem_ctx, &tp); + if (NT_STATUS_IS_OK(status)) { + *blob = tp.out.data; + } + return status; +} + +/**************************************************************************** + Very raw query path info - returns param/data blobs (async send) +****************************************************************************/ +static struct cli_request *smb_raw_pathinfo_blob_send(struct cli_tree *tree, + const char *fname, + uint16 info_level) +{ + struct smb_trans2 tp; + uint16 setup = TRANSACT2_QPATHINFO; + struct cli_request *req; + TALLOC_CTX *mem_ctx = talloc_init("raw_pathinfo"); + + tp.in.max_setup = 0; + tp.in.flags = 0; + tp.in.timeout = 0; + tp.in.setup_count = 1; + tp.in.data = data_blob(NULL, 0); + tp.in.max_param = 2; + tp.in.max_data = 0xFFFF; + tp.in.setup = &setup; + + tp.in.params = data_blob_talloc(mem_ctx, NULL, 6); + if (!tp.in.params.data) { + talloc_destroy(mem_ctx); + return NULL; + } + + SSVAL(tp.in.params.data, 0, info_level); + SIVAL(tp.in.params.data, 2, 0); + cli_blob_append_string(tree->session, mem_ctx, &tp.in.params, + fname, STR_TERMINATE); + + req = smb_raw_trans2_send(tree, &tp); + + talloc_destroy(mem_ctx); + + return req; +} + +/**************************************************************************** + send a SMBgetatr (async send) +****************************************************************************/ +static struct cli_request *smb_raw_getattr_send(struct cli_tree *tree, + union smb_fileinfo *parms) +{ + struct cli_request *req; + + req = cli_request_setup(tree, SMBgetatr, 0, 0); + if (!req) return NULL; + + cli_req_append_ascii4(req, parms->getattr.in.fname, STR_TERMINATE); + + if (!cli_request_send(req)) { + cli_request_destroy(req); + return NULL; + } + + return req; +} + +/**************************************************************************** + send a SMBgetatr (async recv) +****************************************************************************/ +static NTSTATUS smb_raw_getattr_recv(struct cli_request *req, + union smb_fileinfo *parms) +{ + if (!cli_request_receive(req) || + cli_request_is_error(req)) { + return cli_request_destroy(req); + } + + CLI_CHECK_WCT(req, 10); + parms->getattr.out.attrib = SVAL(req->in.vwv, VWV(0)); + parms->getattr.out.write_time = make_unix_date3(req->in.vwv + VWV(1)); + parms->getattr.out.size = IVAL(req->in.vwv, VWV(3)); + +failed: + return cli_request_destroy(req); +} + + +/**************************************************************************** + Handle SMBgetattrE (async send) +****************************************************************************/ +static struct cli_request *smb_raw_getattrE_send(struct cli_tree *tree, + union smb_fileinfo *parms) +{ + struct cli_request *req; + + req = cli_request_setup(tree, SMBgetattrE, 1, 0); + if (!req) return NULL; + + SSVAL(req->out.vwv, VWV(0), parms->getattre.in.fnum); + if (!cli_request_send(req)) { + cli_request_destroy(req); + return NULL; + } + + return req; +} + +/**************************************************************************** + Handle SMBgetattrE (async send) +****************************************************************************/ +static NTSTATUS smb_raw_getattrE_recv(struct cli_request *req, + union smb_fileinfo *parms) +{ + if (!cli_request_receive(req) || + cli_request_is_error(req)) { + return cli_request_destroy(req); + } + + CLI_CHECK_WCT(req, 11); + parms->getattre.out.create_time = make_unix_date2(req->in.vwv + VWV(0)); + parms->getattre.out.access_time = make_unix_date2(req->in.vwv + VWV(2)); + parms->getattre.out.write_time = make_unix_date2(req->in.vwv + VWV(4)); + parms->getattre.out.size = IVAL(req->in.vwv, VWV(6)); + parms->getattre.out.alloc_size = IVAL(req->in.vwv, VWV(8)); + parms->getattre.out.attrib = SVAL(req->in.vwv, VWV(10)); + +failed: + return cli_request_destroy(req); +} + + +/**************************************************************************** + Query file info (async send) +****************************************************************************/ +struct cli_request *smb_raw_fileinfo_send(struct cli_tree *tree, + union smb_fileinfo *parms) +{ + /* pass off the non-trans2 level to specialised functions */ + if (parms->generic.level == RAW_FILEINFO_GETATTRE) { + return smb_raw_getattrE_send(tree, parms); + } + if (parms->generic.level >= RAW_FILEINFO_GENERIC) { + return NULL; + } + + return smb_raw_fileinfo_blob_send(tree, + parms->generic.in.fnum, + parms->generic.level); +} + +/**************************************************************************** + Query file info (async recv) +****************************************************************************/ +NTSTATUS smb_raw_fileinfo_recv(struct cli_request *req, + TALLOC_CTX *mem_ctx, + union smb_fileinfo *parms) +{ + DATA_BLOB blob; + NTSTATUS status; + struct cli_session *session = req?req->session:NULL; + + if (parms->generic.level == RAW_FILEINFO_GETATTRE) { + return smb_raw_getattrE_recv(req, parms); + } + if (parms->generic.level == RAW_FILEINFO_GETATTR) { + return smb_raw_getattr_recv(req, parms); + } + + status = smb_raw_fileinfo_blob_recv(req, mem_ctx, &blob); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + return smb_raw_info_backend(session, mem_ctx, parms, &blob); +} + +/**************************************************************************** + Query file info (sync interface) +****************************************************************************/ +NTSTATUS smb_raw_fileinfo(struct cli_tree *tree, + TALLOC_CTX *mem_ctx, + union smb_fileinfo *parms) +{ + struct cli_request *req = smb_raw_fileinfo_send(tree, parms); + return smb_raw_fileinfo_recv(req, mem_ctx, parms); +} + +/**************************************************************************** + Query path info (async send) +****************************************************************************/ +struct cli_request *smb_raw_pathinfo_send(struct cli_tree *tree, + union smb_fileinfo *parms) +{ + if (parms->generic.level == RAW_FILEINFO_GETATTR) { + return smb_raw_getattr_send(tree, parms); + } + if (parms->generic.level >= RAW_FILEINFO_GENERIC) { + return NULL; + } + + return smb_raw_pathinfo_blob_send(tree, parms->generic.in.fname, + parms->generic.level); +} + +/**************************************************************************** + Query path info (async recv) +****************************************************************************/ +NTSTATUS smb_raw_pathinfo_recv(struct cli_request *req, + TALLOC_CTX *mem_ctx, + union smb_fileinfo *parms) +{ + /* recv is idential to fileinfo */ + return smb_raw_fileinfo_recv(req, mem_ctx, parms); +} + +/**************************************************************************** + Query path info (sync interface) +****************************************************************************/ +NTSTATUS smb_raw_pathinfo(struct cli_tree *tree, + TALLOC_CTX *mem_ctx, + union smb_fileinfo *parms) +{ + struct cli_request *req = smb_raw_pathinfo_send(tree, parms); + return smb_raw_pathinfo_recv(req, mem_ctx, parms); +} diff --git a/source4/libcli/raw/rawfsinfo.c b/source4/libcli/raw/rawfsinfo.c new file mode 100644 index 0000000000..362063bfc5 --- /dev/null +++ b/source4/libcli/raw/rawfsinfo.c @@ -0,0 +1,282 @@ +/* + Unix SMB/CIFS implementation. + + RAW_QFS_* operations + + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 FS Info - SMBdskattr call (async send) +****************************************************************************/ +static struct cli_request *smb_raw_dskattr_send(struct cli_tree *tree, + union smb_fsinfo *fsinfo) +{ + struct cli_request *req; + + req = cli_request_setup(tree, SMBdskattr, 0, 0); + + if (!cli_request_send(req)) { + cli_request_destroy(req); + return NULL; + } + + return req; +} + +/**************************************************************************** + Query FS Info - SMBdskattr call (async recv) +****************************************************************************/ +static NTSTATUS smb_raw_dskattr_recv(struct cli_request *req, + union smb_fsinfo *fsinfo) +{ + if (!cli_request_receive(req) || + cli_request_is_error(req)) { + goto failed; + } + + CLI_CHECK_WCT(req, 5); + fsinfo->dskattr.out.units_total = SVAL(req->in.vwv, VWV(0)); + fsinfo->dskattr.out.blocks_per_unit = SVAL(req->in.vwv, VWV(1)); + fsinfo->dskattr.out.block_size = SVAL(req->in.vwv, VWV(2)); + fsinfo->dskattr.out.units_free = SVAL(req->in.vwv, VWV(3)); + +failed: + return cli_request_destroy(req); +} + + +/**************************************************************************** + RAW_QFS_ trans2 interface via blobs (async send) +****************************************************************************/ +static struct cli_request *smb_raw_qfsinfo_send(struct cli_tree *tree, + TALLOC_CTX *mem_ctx, + uint16 info_level) +{ + struct smb_trans2 tp; + uint16 setup = TRANSACT2_QFSINFO; + + tp.in.max_setup = 0; + tp.in.flags = 0; + tp.in.timeout = 0; + tp.in.setup_count = 1; + tp.in.max_param = 0; + tp.in.max_data = 0x1000; /* plenty for all possible QFS levels */ + tp.in.setup = &setup; + tp.in.data = data_blob(NULL, 0); + tp.in.timeout = 0; + + tp.in.params = data_blob_talloc(mem_ctx, NULL, 2); + if (!tp.in.params.data) { + return NULL; + } + SSVAL(tp.in.params.data, 0, info_level); + + return smb_raw_trans2_send(tree, &tp); +} + +/**************************************************************************** + RAW_QFS_ trans2 interface via blobs (async recv) +****************************************************************************/ +static NTSTATUS smb_raw_qfsinfo_blob_recv(struct cli_request *req, + TALLOC_CTX *mem_ctx, + DATA_BLOB *blob) +{ + struct smb_trans2 tp; + NTSTATUS status; + + status = smb_raw_trans2_recv(req, mem_ctx, &tp); + + if (NT_STATUS_IS_OK(status)) { + (*blob) = tp.out.data; + } + + return status; +} + + +/* local macros to make the code more readable */ +#define QFS_CHECK_MIN_SIZE(size) if (blob.length < (size)) { \ + DEBUG(1,("Unexpected QFS reply size %d for level %u - expected min of %d\n", \ + blob.length, fsinfo->generic.level, (size))); \ + status = NT_STATUS_INFO_LENGTH_MISMATCH; \ + goto failed; \ +} +#define QFS_CHECK_SIZE(size) if (blob.length != (size)) { \ + DEBUG(1,("Unexpected QFS reply size %d for level %u - expected %d\n", \ + blob.length, fsinfo->generic.level, (size))); \ + status = NT_STATUS_INFO_LENGTH_MISMATCH; \ + goto failed; \ +} + + +/**************************************************************************** + Query FSInfo raw interface (async send) +****************************************************************************/ +struct cli_request *smb_raw_fsinfo_send(struct cli_tree *tree, + TALLOC_CTX *mem_ctx, + union smb_fsinfo *fsinfo) +{ + uint16 info_level; + + /* handle the only non-trans2 call separately */ + if (fsinfo->generic.level == RAW_QFS_DSKATTR) { + return smb_raw_dskattr_send(tree, fsinfo); + } + if (fsinfo->generic.level >= RAW_QFS_GENERIC) { + return NULL; + } + + /* the headers map the trans2 levels direct to info levels */ + info_level = (uint16)fsinfo->generic.level; + + return smb_raw_qfsinfo_send(tree, mem_ctx, info_level); +} + + +/**************************************************************************** + Query FSInfo raw interface (async recv) +****************************************************************************/ +NTSTATUS smb_raw_fsinfo_recv(struct cli_request *req, + TALLOC_CTX *mem_ctx, + union smb_fsinfo *fsinfo) +{ + DATA_BLOB blob; + NTSTATUS status; + int i; + struct cli_session *session = req?req->session:NULL; + + if (fsinfo->generic.level == RAW_QFS_DSKATTR) { + return smb_raw_dskattr_recv(req, fsinfo); + } + + status = smb_raw_qfsinfo_blob_recv(req, mem_ctx, &blob); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* parse the results */ + switch (fsinfo->generic.level) { + case RAW_QFS_GENERIC: + case RAW_QFS_DSKATTR: + /* handled above */ + break; + + case RAW_QFS_ALLOCATION: + QFS_CHECK_SIZE(18); + fsinfo->allocation.out.fs_id = IVAL(blob.data, 0); + fsinfo->allocation.out.sectors_per_unit = IVAL(blob.data, 4); + fsinfo->allocation.out.total_alloc_units = IVAL(blob.data, 8); + fsinfo->allocation.out.avail_alloc_units = IVAL(blob.data, 12); + fsinfo->allocation.out.bytes_per_sector = SVAL(blob.data, 16); + break; + + case RAW_QFS_VOLUME: + QFS_CHECK_MIN_SIZE(5); + fsinfo->volume.out.serial_number = IVAL(blob.data, 0); + cli_blob_pull_string(session, mem_ctx, &blob, + &fsinfo->volume.out.volume_name, + 4, 5, STR_LEN8BIT | STR_NOALIGN); + break; + + case RAW_QFS_VOLUME_INFO: + case RAW_QFS_VOLUME_INFORMATION: + QFS_CHECK_MIN_SIZE(18); + fsinfo->volume_info.out.create_time = cli_pull_nttime(blob.data, 0); + fsinfo->volume_info.out.serial_number = IVAL(blob.data, 8); + cli_blob_pull_string(session, mem_ctx, &blob, + &fsinfo->volume_info.out.volume_name, + 12, 18, STR_UNICODE); + break; + + case RAW_QFS_SIZE_INFO: + case RAW_QFS_SIZE_INFORMATION: + QFS_CHECK_SIZE(24); + fsinfo->size_info.out.total_alloc_units = BVAL(blob.data, 0); + fsinfo->size_info.out.avail_alloc_units = BVAL(blob.data, 8); + fsinfo->size_info.out.sectors_per_unit = IVAL(blob.data, 16); + fsinfo->size_info.out.bytes_per_sector = IVAL(blob.data, 20); + break; + + case RAW_QFS_DEVICE_INFO: + case RAW_QFS_DEVICE_INFORMATION: + QFS_CHECK_SIZE(8); + fsinfo->device_info.out.device_type = IVAL(blob.data, 0); + fsinfo->device_info.out.characteristics = IVAL(blob.data, 4); + break; + + case RAW_QFS_ATTRIBUTE_INFO: + case RAW_QFS_ATTRIBUTE_INFORMATION: + QFS_CHECK_MIN_SIZE(12); + fsinfo->attribute_info.out.fs_attr = IVAL(blob.data, 0); + fsinfo->attribute_info.out.max_file_component_length = IVAL(blob.data, 4); + cli_blob_pull_string(session, mem_ctx, &blob, + &fsinfo->attribute_info.out.fs_type, + 8, 12, STR_UNICODE); + break; + + case RAW_QFS_UNIX_INFO: + QFS_CHECK_SIZE(12); + fsinfo->unix_info.out.major_version = SVAL(blob.data, 0); + fsinfo->unix_info.out.minor_version = SVAL(blob.data, 2); + fsinfo->unix_info.out.capability = SVAL(blob.data, 4); + break; + + case RAW_QFS_QUOTA_INFORMATION: + QFS_CHECK_SIZE(48); + fsinfo->quota_information.out.unknown[0] = BVAL(blob.data, 0); + fsinfo->quota_information.out.unknown[1] = BVAL(blob.data, 8); + fsinfo->quota_information.out.unknown[2] = BVAL(blob.data, 16); + fsinfo->quota_information.out.quota_soft = BVAL(blob.data, 24); + fsinfo->quota_information.out.quota_hard = BVAL(blob.data, 32); + fsinfo->quota_information.out.quota_flags = BVAL(blob.data, 40); + break; + + case RAW_QFS_FULL_SIZE_INFORMATION: + QFS_CHECK_SIZE(32); + fsinfo->full_size_information.out.total_alloc_units = BVAL(blob.data, 0); + fsinfo->full_size_information.out.call_avail_alloc_units = BVAL(blob.data, 8); + fsinfo->full_size_information.out.actual_avail_alloc_units = BVAL(blob.data, 16); + fsinfo->full_size_information.out.sectors_per_unit = IVAL(blob.data, 24); + fsinfo->full_size_information.out.bytes_per_sector = IVAL(blob.data, 28); + break; + + case RAW_QFS_OBJECTID_INFORMATION: + QFS_CHECK_SIZE(64); + memcpy(fsinfo->objectid_information.out.guid.info, blob.data, GUID_SIZE); + for (i=0;i<6;i++) { + fsinfo->objectid_information.out.unknown[i] = BVAL(blob.data, 16 + i*8); + } + break; + } + +failed: + return status; +} + +/**************************************************************************** + Query FSInfo raw interface (sync interface) +****************************************************************************/ +NTSTATUS smb_raw_fsinfo(struct cli_tree *tree, + TALLOC_CTX *mem_ctx, + union smb_fsinfo *fsinfo) +{ + struct cli_request *req = smb_raw_fsinfo_send(tree, mem_ctx, fsinfo); + return smb_raw_fsinfo_recv(req, mem_ctx, fsinfo); +} diff --git a/source4/libcli/raw/rawioctl.c b/source4/libcli/raw/rawioctl.c new file mode 100644 index 0000000000..506bddd497 --- /dev/null +++ b/source4/libcli/raw/rawioctl.c @@ -0,0 +1,118 @@ +/* + Unix SMB/CIFS implementation. + client file operations + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 SETUP_REQUEST(cmd, wct, buflen) do { \ + req = cli_request_setup(tree, cmd, wct, buflen); \ + if (!req) return NULL; \ +} while (0) + +/* + send a raw ioctl - async send +*/ +struct cli_request *smb_raw_ioctl_send(struct cli_tree *tree, struct smb_ioctl *parms) +{ + struct cli_request *req; + + SETUP_REQUEST(SMBioctl, 3, 0); + + SSVAL(req->out.vwv, VWV(0), parms->in.fnum); + SIVAL(req->out.vwv, VWV(1), parms->in.request); + + if (!cli_request_send(req)) { + cli_request_destroy(req); + return NULL; + } + + return req; +} + +/* + send a raw ioctl - async recv +*/ +NTSTATUS smb_raw_ioctl_recv(struct cli_request *req, TALLOC_CTX *mem_ctx, struct smb_ioctl *parms) +{ + if (!cli_request_receive(req) || + cli_request_is_error(req)) { + return cli_request_destroy(req); + } + + parms->out.blob = cli_req_pull_blob(req, mem_ctx, req->in.data, -1); + return cli_request_destroy(req); +} + +/* + send a raw ioctl - sync interface +*/ +NTSTATUS smb_raw_ioctl(struct cli_tree *tree, TALLOC_CTX *mem_ctx, struct smb_ioctl *parms) +{ + struct cli_request *req = smb_raw_ioctl_send(tree, parms); + return smb_raw_ioctl_recv(req, mem_ctx, parms); +} + + + + +/**************************************************************************** +NT ioctl (async send) +****************************************************************************/ +struct cli_request *smb_raw_ntioctl_send(struct cli_tree *tree, + struct smb_ntioctl *parms) +{ + struct smb_nttrans nt; + uint16 setup[4]; + + nt.in.max_setup = 0; + nt.in.max_param = 0; + nt.in.max_data = 0; + nt.in.setup_count = 4; + nt.in.setup = setup; + SIVAL(setup, 0, parms->in.function); + SSVAL(setup, 4, parms->in.fnum); + SCVAL(setup, 6, parms->in.fsctl); + SCVAL(setup, 7, parms->in.filter); + nt.in.function = NT_TRANSACT_IOCTL; + nt.in.params = data_blob(NULL, 0); + nt.in.data = data_blob(NULL, 0); + + return smb_raw_nttrans_send(tree, &nt); +} + +/**************************************************************************** +NT ioctl (async recv) +****************************************************************************/ +NTSTATUS smb_raw_ntioctl_recv(struct cli_request *req, + struct smb_ntioctl *parms) +{ + struct smb_nttrans nt; + + return smb_raw_nttrans_recv(req, req->mem_ctx, &nt); +} + +/**************************************************************************** +NT ioctl (sync interface) +****************************************************************************/ +NTSTATUS smb_raw_ntioctl(struct cli_tree *tree, + struct smb_ntioctl *parms) +{ + struct cli_request *req = smb_raw_ntioctl_send(tree, parms); + return smb_raw_ntioctl_recv(req, parms); +} diff --git a/source4/libcli/raw/rawnegotiate.c b/source4/libcli/raw/rawnegotiate.c new file mode 100644 index 0000000000..78b2e00706 --- /dev/null +++ b/source4/libcli/raw/rawnegotiate.c @@ -0,0 +1,157 @@ +/* + Unix SMB/CIFS implementation. + SMB client negotiate context management functions + Copyright (C) Andrew Tridgell 1994-1998 + Copyright (C) James Myers 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 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_LANMAN1,"Windows for Workgroups 3.1a"}, + {PROTOCOL_LANMAN2,"LM1.2X002"}, + {PROTOCOL_LANMAN2,"DOS LANMAN2.1"}, + {PROTOCOL_LANMAN2,"Samba"}, + {PROTOCOL_NT1,"NT LANMAN 1.0"}, + {PROTOCOL_NT1,"NT LM 0.12"}, +}; + +/**************************************************************************** + Send a negprot command. +****************************************************************************/ +struct cli_request *smb_negprot_send(struct cli_transport *transport, int maxprotocol) +{ + struct cli_request *req; + int i; + + req = cli_request_setup_transport(transport, SMBnegprot, 0, 0); + if (!req) { + return NULL; + } + + /* setup the protocol strings */ + for (i=0; i < ARRAY_SIZE(prots) && prots[i].prot <= maxprotocol; i++) { + cli_req_append_bytes(req, "\2", 1); + cli_req_append_string(req, prots[i].name, STR_TERMINATE | STR_ASCII); + } + + if (!cli_request_send(req)) { + cli_request_destroy(req); + return NULL; + } + + return req; +} + +/**************************************************************************** + Send a negprot command. +****************************************************************************/ +NTSTATUS smb_raw_negotiate(struct cli_transport *transport) +{ + struct cli_request *req; + int protocol; + + req = smb_negprot_send(transport, PROTOCOL_NT1); + if (!req) { + return NT_STATUS_UNSUCCESSFUL; + } + + if (!cli_request_receive(req) || + cli_request_is_error(req)) { + return cli_request_destroy(req); + } + + CLI_CHECK_MIN_WCT(req, 1); + + protocol = SVALS(req->in.vwv, VWV(0)); + + if (protocol >= ARRAY_SIZE(prots) || protocol < 0) { + req->status = NT_STATUS_UNSUCCESSFUL; + return cli_request_destroy(req); + } + + transport->negotiate.protocol = prots[protocol].prot; + + if (transport->negotiate.protocol >= PROTOCOL_NT1) { + NTTIME ntt; + + /* NT protocol */ + CLI_CHECK_WCT(req, 17); + transport->negotiate.sec_mode = CVAL(req->in.vwv,VWV(1)); + transport->negotiate.max_mux = SVAL(req->in.vwv,VWV(1)+1); + transport->negotiate.max_xmit = IVAL(req->in.vwv,VWV(3)+1); + transport->negotiate.sesskey = IVAL(req->in.vwv,VWV(7)+1); + transport->negotiate.server_zone = SVALS(req->in.vwv,VWV(15)+1) * 60; + + /* this time arrives in real GMT */ + ntt = cli_pull_nttime(req->in.vwv, VWV(11)+1); + transport->negotiate.server_time = nt_time_to_unix(&ntt); + transport->negotiate.capabilities = IVAL(req->in.vwv,VWV(9)+1); + + transport->negotiate.secblob = cli_req_pull_blob(req, transport->mem_ctx, req->in.data, req->in.data_size); + if (transport->negotiate.capabilities & CAP_RAW_MODE) { + transport->negotiate.readbraw_supported = True; + transport->negotiate.writebraw_supported = True; + } + + /* work out if they sent us a workgroup */ + if ((transport->negotiate.capabilities & CAP_EXTENDED_SECURITY) && + req->in.data_size > 16) { + cli_req_pull_string(req, transport->mem_ctx, &transport->negotiate.server_domain, + req->in.data+16, + req->in.data_size-16, STR_UNICODE|STR_NOALIGN); + } + } else if (transport->negotiate.protocol >= PROTOCOL_LANMAN1) { + CLI_CHECK_WCT(req, 13); + transport->negotiate.sec_mode = SVAL(req->in.vwv,VWV(1)); + transport->negotiate.max_xmit = SVAL(req->in.vwv,VWV(2)); + transport->negotiate.sesskey = IVAL(req->in.vwv,VWV(6)); + transport->negotiate.server_zone = SVALS(req->in.vwv,VWV(10)) * 60; + + /* this time is converted to GMT by make_unix_date */ + transport->negotiate.server_time = make_unix_date(req->in.vwv+VWV(8)); + if ((SVAL(req->in.vwv,VWV(5)) & 0x1)) { + transport->negotiate.readbraw_supported = 1; + } + if ((SVAL(req->in.vwv,VWV(5)) & 0x2)) { + transport->negotiate.writebraw_supported = 1; + } + transport->negotiate.secblob = cli_req_pull_blob(req, transport->mem_ctx, + req->in.data, req->in.data_size); + } else { + /* the old core protocol */ + transport->negotiate.sec_mode = 0; + transport->negotiate.server_time = time(NULL); + transport->negotiate.max_xmit = ~0; + transport->negotiate.server_zone = TimeDiff(time(NULL)); + } + + /* a way to force ascii SMB */ + if (getenv("CLI_FORCE_ASCII")) { + transport->negotiate.capabilities &= ~CAP_UNICODE; + } + +failed: + return cli_request_destroy(req); +} diff --git a/source4/libcli/raw/rawnotify.c b/source4/libcli/raw/rawnotify.c new file mode 100644 index 0000000000..7d635da0dc --- /dev/null +++ b/source4/libcli/raw/rawnotify.c @@ -0,0 +1,116 @@ +/* + Unix SMB/CIFS implementation. + client change notify operations + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 notify (async send) +****************************************************************************/ +struct cli_request *smb_raw_changenotify_send(struct cli_tree *tree, struct smb_notify *parms) +{ + struct smb_nttrans nt; + uint16 setup[4]; + + nt.in.max_setup = 0; + nt.in.max_param = parms->in.buffer_size; + nt.in.max_data = 0; + nt.in.setup_count = 4; + nt.in.setup = setup; + SIVAL(setup, 0, parms->in.completion_filter); + SSVAL(setup, 4, parms->in.fnum); + SSVAL(setup, 6, parms->in.recursive); + nt.in.function = NT_TRANSACT_NOTIFY_CHANGE; + nt.in.params = data_blob(NULL, 0); + nt.in.data = data_blob(NULL, 0); + + return smb_raw_nttrans_send(tree, &nt); +} + +/**************************************************************************** +change notify (async recv) +****************************************************************************/ +NTSTATUS smb_raw_changenotify_recv(struct cli_request *req, + TALLOC_CTX *mem_ctx, struct smb_notify *parms) +{ + struct smb_nttrans nt; + NTSTATUS status; + uint32 ofs, i; + struct cli_session *session = req?req->session:NULL; + + status = smb_raw_nttrans_recv(req, mem_ctx, &nt); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + parms->out.changes = NULL; + parms->out.num_changes = 0; + + /* count them */ + for (ofs=0; nt.out.params.length - ofs > 12; ) { + uint32 next = IVAL(nt.out.params.data, ofs); + parms->out.num_changes++; + if (next == 0 || + ofs + next >= nt.out.params.length) break; + ofs += next; + } + + /* allocate array */ + parms->out.changes = talloc(mem_ctx, sizeof(parms->out.changes[0]) * + parms->out.num_changes); + if (!parms->out.changes) { + return NT_STATUS_NO_MEMORY; + } + + for (i=ofs=0; iout.num_changes; i++) { + parms->out.changes[i].action = IVAL(nt.out.params.data, ofs+4); + cli_blob_pull_string(session, mem_ctx, &nt.out.params, + &parms->out.changes[i].name, + ofs+8, ofs+12, STR_UNICODE); + ofs += IVAL(nt.out.params.data, ofs); + } + + return NT_STATUS_OK; +} + + +/**************************************************************************** + Send a NT Cancel request - used to hurry along a pending request. Usually + used to cancel a pending change notify request + note that this request does not expect a response! +****************************************************************************/ +NTSTATUS smb_raw_ntcancel(struct cli_request *oldreq) +{ + struct cli_request *req; + + req = cli_request_setup_transport(oldreq->transport, SMBntcancel, 0, 0); + + SSVAL(req->out.hdr, HDR_MID, SVAL(oldreq->out.hdr, HDR_MID)); + SSVAL(req->out.hdr, HDR_PID, SVAL(oldreq->out.hdr, HDR_PID)); + SSVAL(req->out.hdr, HDR_TID, SVAL(oldreq->out.hdr, HDR_TID)); + SSVAL(req->out.hdr, HDR_UID, SVAL(oldreq->out.hdr, HDR_UID)); + + /* this request does not expect a reply, so tell the signing + subsystem not to allocate an id for a reply */ + req->one_way_request = 1; + + cli_request_send(req); + + return cli_request_destroy(req); +} diff --git a/source4/libcli/raw/rawreadwrite.c b/source4/libcli/raw/rawreadwrite.c new file mode 100644 index 0000000000..84c7e3c00f --- /dev/null +++ b/source4/libcli/raw/rawreadwrite.c @@ -0,0 +1,321 @@ +/* + Unix SMB/CIFS implementation. + client file read/write routines + Copyright (C) Andrew Tridgell 1994-1998 + Copyright (C) James Myers 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 SETUP_REQUEST(cmd, wct, buflen) do { \ + req = cli_request_setup(tree, cmd, wct, buflen); \ + if (!req) return NULL; \ +} while (0) + + +/**************************************************************************** + low level read operation (async send) +****************************************************************************/ +struct cli_request *smb_raw_read_send(struct cli_tree *tree, union smb_read *parms) +{ + BOOL bigoffset = False; + struct cli_request *req; + + switch (parms->generic.level) { + case RAW_READ_GENERIC: + return NULL; + + case RAW_READ_READBRAW: + if (parms->readbraw.in.offset >= 0x80000000) { + bigoffset = True; + } + SETUP_REQUEST(SMBreadbraw, bigoffset? 10:8, 0); + SSVAL(req->out.vwv, VWV(0), parms->readbraw.in.fnum); + SIVAL(req->out.vwv, VWV(1), parms->readbraw.in.offset); + SSVAL(req->out.vwv, VWV(3), parms->readbraw.in.maxcnt); + SSVAL(req->out.vwv, VWV(4), parms->readbraw.in.mincnt); + SIVAL(req->out.vwv, VWV(5), parms->readbraw.in.timeout); + SSVAL(req->out.vwv, VWV(7), 0); /* reserved */ + if (bigoffset) { + SIVAL(req->out.vwv, VWV(8),parms->readbraw.in.offset>>32); + } + break; + + case RAW_READ_LOCKREAD: + SETUP_REQUEST(SMBlockread, 5, 0); + SSVAL(req->out.vwv, VWV(0), parms->lockread.in.fnum); + SSVAL(req->out.vwv, VWV(1), parms->lockread.in.count); + SIVAL(req->out.vwv, VWV(2), parms->lockread.in.offset); + SSVAL(req->out.vwv, VWV(4), parms->lockread.in.remaining); + break; + + case RAW_READ_READ: + SETUP_REQUEST(SMBread, 5, 0); + SSVAL(req->out.vwv, VWV(0), parms->read.in.fnum); + SSVAL(req->out.vwv, VWV(1), parms->read.in.count); + SIVAL(req->out.vwv, VWV(2), parms->read.in.offset); + SSVAL(req->out.vwv, VWV(4), parms->read.in.remaining); + break; + + case RAW_READ_READX: + if (parms->readx.in.offset >= 0x80000000) { + bigoffset = True; + } + SETUP_REQUEST(SMBreadX, bigoffset ? 12 : 10, 0); + SSVAL(req->out.vwv, VWV(0), 0xFF); + SSVAL(req->out.vwv, VWV(1), 0); + SSVAL(req->out.vwv, VWV(2), parms->readx.in.fnum); + SIVAL(req->out.vwv, VWV(3), parms->readx.in.offset); + SSVAL(req->out.vwv, VWV(5), parms->readx.in.maxcnt); + SSVAL(req->out.vwv, VWV(6), parms->readx.in.mincnt); + SIVAL(req->out.vwv, VWV(7), 0); /* reserved */ + SSVAL(req->out.vwv, VWV(9), parms->readx.in.remaining); + if (bigoffset) { + SIVAL(req->out.vwv, VWV(10),parms->readx.in.offset>>32); + } + break; + } + + if (!cli_request_send(req)) { + cli_request_destroy(req); + return NULL; + } + + /* the transport layer needs to know that a readbraw is pending + and handle receives a little differently */ + if (parms->generic.level == RAW_READ_READBRAW) { + tree->session->transport->readbraw_pending = 1; + } + + return req; +} + +/**************************************************************************** + low level read operation (async recv) +****************************************************************************/ +NTSTATUS smb_raw_read_recv(struct cli_request *req, union smb_read *parms) +{ + if (!cli_request_receive(req) || + cli_request_is_error(req)) { + goto failed; + } + + switch (parms->generic.level) { + case RAW_READ_GENERIC: + /* handled in _send() */ + break; + + case RAW_READ_READBRAW: + parms->readbraw.out.nread = req->in.size - NBT_HDR_SIZE; + if (parms->readbraw.out.nread > + MAX(parms->readx.in.mincnt, parms->readx.in.maxcnt)) { + req->status = NT_STATUS_BUFFER_TOO_SMALL; + goto failed; + } + memcpy(parms->readbraw.out.data, req->in.buffer + NBT_HDR_SIZE, parms->readbraw.out.nread); + break; + + case RAW_READ_LOCKREAD: + CLI_CHECK_WCT(req, 5); + parms->lockread.out.nread = SVAL(req->in.vwv, VWV(0)); + if (parms->lockread.out.nread > parms->lockread.in.count || + !cli_raw_pull_data(req, req->in.data+3, + parms->lockread.out.nread, parms->lockread.out.data)) { + req->status = NT_STATUS_BUFFER_TOO_SMALL; + } + break; + + case RAW_READ_READ: + /* there are 4 reserved words in the reply */ + CLI_CHECK_WCT(req, 5); + parms->read.out.nread = SVAL(req->in.vwv, VWV(0)); + if (parms->read.out.nread > parms->read.in.count || + !cli_raw_pull_data(req, req->in.data+3, + parms->read.out.nread, parms->read.out.data)) { + req->status = NT_STATUS_BUFFER_TOO_SMALL; + } + break; + + case RAW_READ_READX: + /* there are 5 reserved words in the reply */ + CLI_CHECK_WCT(req, 12); + parms->readx.out.remaining = SVAL(req->in.vwv, VWV(2)); + parms->readx.out.compaction_mode = SVAL(req->in.vwv, VWV(3)); + parms->readx.out.nread = SVAL(req->in.vwv, VWV(5)); + if (parms->readx.out.nread > MAX(parms->readx.in.mincnt, parms->readx.in.maxcnt) || + !cli_raw_pull_data(req, req->in.hdr + SVAL(req->in.vwv, VWV(6)), + parms->readx.out.nread, + parms->readx.out.data)) { + req->status = NT_STATUS_BUFFER_TOO_SMALL; + } + break; + } + +failed: + return cli_request_destroy(req); +} + +/**************************************************************************** + low level read operation (sync interface) +****************************************************************************/ +NTSTATUS smb_raw_read(struct cli_tree *tree, union smb_read *parms) +{ + struct cli_request *req = smb_raw_read_send(tree, parms); + return smb_raw_read_recv(req, parms); +} + + +/**************************************************************************** + raw write interface (async send) +****************************************************************************/ +struct cli_request *smb_raw_write_send(struct cli_tree *tree, union smb_write *parms) +{ + BOOL bigoffset = False; + struct cli_request *req; + + switch (parms->generic.level) { + case RAW_WRITE_GENERIC: + return NULL; + + case RAW_WRITE_WRITEUNLOCK: + SETUP_REQUEST(SMBwriteunlock, 5, 3 + parms->writeunlock.in.count); + SSVAL(req->out.vwv, VWV(0), parms->writeunlock.in.fnum); + SSVAL(req->out.vwv, VWV(1), parms->writeunlock.in.count); + SIVAL(req->out.vwv, VWV(2), parms->writeunlock.in.offset); + SSVAL(req->out.vwv, VWV(4), parms->writeunlock.in.remaining); + SCVAL(req->out.data, 0, SMB_DATA_BLOCK); + SSVAL(req->out.data, 1, parms->writeunlock.in.count); + if (parms->writeunlock.in.count > 0) { + memcpy(req->out.data+3, parms->writeunlock.in.data, + parms->writeunlock.in.count); + } + break; + + case RAW_WRITE_WRITE: + SETUP_REQUEST(SMBwrite, 5, 3 + parms->write.in.count); + SSVAL(req->out.vwv, VWV(0), parms->write.in.fnum); + SSVAL(req->out.vwv, VWV(1), parms->write.in.count); + SIVAL(req->out.vwv, VWV(2), parms->write.in.offset); + SSVAL(req->out.vwv, VWV(4), parms->write.in.remaining); + SCVAL(req->out.data, 0, SMB_DATA_BLOCK); + SSVAL(req->out.data, 1, parms->write.in.count); + if (parms->write.in.count > 0) { + memcpy(req->out.data+3, parms->write.in.data, parms->write.in.count); + } + break; + + case RAW_WRITE_WRITECLOSE: + SETUP_REQUEST(SMBwriteclose, 6, 1 + parms->writeclose.in.count); + SSVAL(req->out.vwv, VWV(0), parms->writeclose.in.fnum); + SSVAL(req->out.vwv, VWV(1), parms->writeclose.in.count); + SIVAL(req->out.vwv, VWV(2), parms->writeclose.in.offset); + put_dos_date3(req->out.vwv, VWV(4), parms->writeclose.in.mtime); + SCVAL(req->out.data, 0, 0); + if (parms->writeclose.in.count > 0) { + memcpy(req->out.data+1, parms->writeclose.in.data, + parms->writeclose.in.count); + } + break; + + case RAW_WRITE_WRITEX: + if (parms->writex.in.offset >= 0x80000000) { + bigoffset = True; + } + SETUP_REQUEST(SMBwriteX, bigoffset ? 14 : 12, parms->writex.in.count); + SSVAL(req->out.vwv, VWV(0), 0xFF); + SSVAL(req->out.vwv, VWV(1), 0); + SSVAL(req->out.vwv, VWV(2), parms->writex.in.fnum); + SIVAL(req->out.vwv, VWV(3), parms->writex.in.offset); + SIVAL(req->out.vwv, VWV(5), 0); /* reserved */ + SSVAL(req->out.vwv, VWV(7), parms->writex.in.wmode); + SSVAL(req->out.vwv, VWV(8), parms->writex.in.remaining); + SSVAL(req->out.vwv, VWV(9), 0); /* reserved */ + SSVAL(req->out.vwv, VWV(10), parms->writex.in.count); + SSVAL(req->out.vwv, VWV(11), PTR_DIFF(req->out.data, req->out.hdr)); + if (bigoffset) { + SIVAL(req->out.vwv,VWV(12),parms->writex.in.offset>>32); + } + if (parms->writex.in.count > 0) { + memcpy(req->out.data, parms->writex.in.data, parms->writex.in.count); + } + break; + + case RAW_WRITE_SPLWRITE: + SETUP_REQUEST(SMBsplwr, 1, parms->splwrite.in.count); + SSVAL(req->out.vwv, VWV(0), parms->splwrite.in.fnum); + if (parms->splwrite.in.count > 0) { + memcpy(req->out.data, parms->splwrite.in.data, parms->splwrite.in.count); + } + break; + } + + if (!cli_request_send(req)) { +cli_request_destroy(req); + return NULL; + } + + return req; +} + + +/**************************************************************************** + raw write interface (async recv) +****************************************************************************/ +NTSTATUS smb_raw_write_recv(struct cli_request *req, union smb_write *parms) +{ + if (!cli_request_receive(req) || + cli_request_is_error(req)) { + goto failed; + } + + switch (parms->generic.level) { + case RAW_WRITE_GENERIC: + break; + case RAW_WRITE_WRITEUNLOCK: + CLI_CHECK_WCT(req, 1); + parms->writeunlock.out.nwritten = SVAL(req->in.vwv, VWV(0)); + break; + case RAW_WRITE_WRITE: + CLI_CHECK_WCT(req, 1); + parms->write.out.nwritten = SVAL(req->in.vwv, VWV(0)); + break; + case RAW_WRITE_WRITECLOSE: + CLI_CHECK_WCT(req, 1); + parms->writeclose.out.nwritten = SVAL(req->in.vwv, VWV(0)); + break; + case RAW_WRITE_WRITEX: + CLI_CHECK_WCT(req, 6); + parms->writex.out.nwritten = SVAL(req->in.vwv, VWV(2)); + parms->writex.out.nwritten += (CVAL(req->in.vwv, VWV(4)) << 16); + parms->writex.out.remaining = SVAL(req->in.vwv, VWV(3)); + break; + case RAW_WRITE_SPLWRITE: + break; + } + +failed: + return cli_request_destroy(req); +} + +/**************************************************************************** + raw write interface (sync interface) +****************************************************************************/ +NTSTATUS smb_raw_write(struct cli_tree *tree, union smb_write *parms) +{ + struct cli_request *req = smb_raw_write_send(tree, parms); + return smb_raw_write_recv(req, parms); +} diff --git a/source4/libcli/raw/rawrequest.c b/source4/libcli/raw/rawrequest.c new file mode 100644 index 0000000000..9c2b2c7367 --- /dev/null +++ b/source4/libcli/raw/rawrequest.c @@ -0,0 +1,1019 @@ +/* + Unix SMB/CIFS implementation. + + Copyright (C) Andrew Tridgell 2003 + Copyright (C) James Myers 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the 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 implements functions for manipulating the 'struct cli_request' structure in libsmb +*/ + +#include "includes.h" + +/* we over allocate the data buffer to prevent too many realloc calls */ +#define REQ_OVER_ALLOCATION 256 + +/* assume that a character will not consume more than 3 bytes per char */ +#define MAX_BYTES_PER_CHAR 3 + +/* destroy a request structure and return final status */ +NTSTATUS cli_request_destroy(struct cli_request *req) +{ + NTSTATUS status; + + /* this is the error code we give the application for when a + _send() call fails completely */ + if (!req) return NT_STATUS_UNSUCCESSFUL; + + /* remove it from the list of pending requests (a null op if + its not in the list) */ + DLIST_REMOVE(req->transport->pending_requests, req); + + /* ahh, its so nice to destroy a complex structure in such a + simple way! */ + status = req->status; + talloc_destroy(req->mem_ctx); + return status; +} + + +/* + low-level function to setup a request buffer for a non-SMB packet + at the transport level +*/ +struct cli_request *cli_request_setup_nonsmb(struct cli_transport *transport, uint_t size) +{ + struct cli_request *req; + TALLOC_CTX *mem_ctx; + + /* each request gets its own talloc context. The request + structure itself is also allocated inside this context, + so we need to allocate it before we construct the request + */ + mem_ctx = talloc_init("cli_request"); + if (!mem_ctx) { + return NULL; + } + + req = talloc(mem_ctx, sizeof(struct cli_request)); + if (!req) { + return NULL; + } + ZERO_STRUCTP(req); + + /* setup the request context */ + req->mem_ctx = mem_ctx; + req->transport = transport; + req->session = NULL; + req->tree = NULL; + req->out.size = size; + + /* over allocate by a small amount */ + req->out.allocated = req->out.size + REQ_OVER_ALLOCATION; + + req->out.buffer = talloc(req->mem_ctx, req->out.allocated); + if (!req->out.buffer) { + return NULL; + } + + SIVAL(req->out.buffer, 0, 0); + + return req; +} + + +/* + setup a SMB packet at transport level +*/ +struct cli_request *cli_request_setup_transport(struct cli_transport *transport, + uint8 command, unsigned wct, unsigned buflen) +{ + struct cli_request *req; + + req = cli_request_setup_nonsmb(transport, NBT_HDR_SIZE + MIN_SMB_SIZE + wct*2 + buflen); + + if (!req) return NULL; + + req->out.hdr = req->out.buffer + NBT_HDR_SIZE; + req->out.vwv = req->out.hdr + HDR_VWV; + req->out.wct = wct; + req->out.data = req->out.vwv + VWV(wct) + 2; + req->out.data_size = buflen; + req->out.ptr = req->out.data; + + SCVAL(req->out.hdr, HDR_WCT, wct); + SSVAL(req->out.vwv, VWV(wct), buflen); + + memcpy(req->out.hdr, "\377SMB", 4); + SCVAL(req->out.hdr,HDR_COM,command); + + SCVAL(req->out.hdr,HDR_FLG, FLAG_CASELESS_PATHNAMES); + SSVAL(req->out.hdr,HDR_FLG2, 0); + + /* assign a mid */ + req->mid = cli_transport_next_mid(transport); + + /* copy the pid, uid and mid to the request */ + SSVAL(req->out.hdr, HDR_PID, 0); + SSVAL(req->out.hdr, HDR_UID, 0); + SSVAL(req->out.hdr, HDR_MID, req->mid); + SSVAL(req->out.hdr, HDR_TID,0); + SSVAL(req->out.hdr, HDR_PIDHIGH,0); + SIVAL(req->out.hdr, HDR_RCLS, 0); + memset(req->out.hdr+HDR_SS_FIELD, 0, 10); + + return req; +} + +/* + setup a reply in req->out with the given word count and initial data + buffer size. the caller will then fill in the command words and + data before calling cli_request_send() to send the reply on its + way. This interface is used before a session is setup. +*/ +struct cli_request *cli_request_setup_session(struct cli_session *session, + uint8 command, unsigned wct, unsigned buflen) +{ + struct cli_request *req; + uint16 flags2; + uint32 capabilities; + + req = cli_request_setup_transport(session->transport, command, wct, buflen); + + if (!req) return NULL; + + req->session = session; + + flags2 = FLAGS2_LONG_PATH_COMPONENTS; + capabilities = session->transport->negotiate.capabilities; + + if (capabilities & CAP_UNICODE) { + flags2 |= FLAGS2_UNICODE_STRINGS; + } + if (capabilities & CAP_STATUS32) { + flags2 |= FLAGS2_32_BIT_ERROR_CODES; + } + if (capabilities & CAP_EXTENDED_SECURITY) { + flags2 |= FLAGS2_EXTENDED_SECURITY; + } + if (session->transport->negotiate.sign_info.doing_signing) { + flags2 |= FLAGS2_SMB_SECURITY_SIGNATURES; + } + + SSVAL(req->out.hdr, HDR_FLG2, flags2); + SSVAL(req->out.hdr, HDR_PID, session->pid); + SSVAL(req->out.hdr, HDR_UID, session->vuid); + + return req; +} + +/* + setup a request for tree based commands +*/ +struct cli_request *cli_request_setup(struct cli_tree *tree, + uint8 command, + unsigned wct, unsigned buflen) +{ + struct cli_request *req; + + req = cli_request_setup_session(tree->session, command, wct, buflen); + if (req) { + req->tree = tree; + SSVAL(req->out.hdr,HDR_TID,tree->tid); + } + return req; +} + +/* + grow the allocation of the data buffer portion of a reply + packet. Note that as this can reallocate the packet buffer this + invalidates any local pointers into the packet. + + To cope with this req->out.ptr is supplied. This will be updated to + point at the same offset into the packet as before this call +*/ +static void cli_req_grow_allocation(struct cli_request *req, unsigned new_size) +{ + int delta; + char *buf2; + + delta = new_size - req->out.data_size; + if (delta + req->out.size <= req->out.allocated) { + /* it fits in the preallocation */ + return; + } + + /* we need to realloc */ + req->out.allocated = req->out.size + delta + REQ_OVER_ALLOCATION; + buf2 = talloc_realloc(req->mem_ctx, req->out.buffer, req->out.allocated); + if (buf2 == NULL) { + smb_panic("out of memory in req_grow_allocation"); + } + + if (buf2 == req->out.buffer) { + /* the malloc library gave us the same pointer */ + return; + } + + /* update the pointers into the packet */ + req->out.data = buf2 + PTR_DIFF(req->out.data, req->out.buffer); + req->out.ptr = buf2 + PTR_DIFF(req->out.ptr, req->out.buffer); + req->out.vwv = buf2 + PTR_DIFF(req->out.vwv, req->out.buffer); + req->out.hdr = buf2 + PTR_DIFF(req->out.hdr, req->out.buffer); + + req->out.buffer = buf2; +} + + +/* + grow the data buffer portion of a reply packet. Note that as this + can reallocate the packet buffer this invalidates any local pointers + into the packet. + + To cope with this req->out.ptr is supplied. This will be updated to + point at the same offset into the packet as before this call +*/ +static void cli_req_grow_data(struct cli_request *req, unsigned new_size) +{ + int delta; + + cli_req_grow_allocation(req, new_size); + + delta = new_size - req->out.data_size; + + req->out.size += delta; + req->out.data_size += delta; + + /* set the BCC to the new data size */ + SSVAL(req->out.vwv, VWV(req->out.wct), new_size); +} + +/* + send a message +*/ +BOOL cli_request_send(struct cli_request *req) +{ + if (IVAL(req->out.buffer, 0) == 0) { + _smb_setlen(req->out.buffer, req->out.size - NBT_HDR_SIZE); + } + + cli_request_calculate_sign_mac(req); + + if (req->out.size != cli_sock_write(req->transport->socket, req->out.buffer, req->out.size)) { + req->transport->error.etype = ETYPE_SOCKET; + req->transport->error.e.socket_error = SOCKET_WRITE_ERROR; + DEBUG(0,("Error writing %d bytes to server - %s\n", + (int)req->out.size, strerror(errno))); + return False; + } + + /* add it to the list of pending requests */ + DLIST_ADD(req->transport->pending_requests, req); + + return True; +} + + +/* + receive a response to a packet +*/ +BOOL cli_request_receive(struct cli_request *req) +{ + /* req can be NULL when a send has failed. This eliminates lots of NULL + checks in each module */ + if (!req) return False; + + /* keep receiving packets until this one is replied to */ + while (!req->in.buffer) { + if (!cli_transport_select(req->transport)) { + return False; + } + + cli_request_receive_next(req->transport); + } + + return True; +} + + +/* + handle oplock break requests from the server - return True if the request was + an oplock break +*/ +static BOOL handle_oplock_break(struct cli_transport *transport, uint_t len, const char *hdr, const char *vwv) +{ + /* we must be very fussy about what we consider an oplock break to avoid + matching readbraw replies */ + if (len != MIN_SMB_SIZE + VWV(8) || + (CVAL(hdr, HDR_FLG) & FLAG_REPLY) || + CVAL(hdr,HDR_COM) != SMBlockingX || + SVAL(hdr, HDR_MID) != 0xFFFF || + SVAL(vwv,VWV(6)) != 0 || + SVAL(vwv,VWV(7)) != 0) { + return False; + } + + if (transport->oplock.handler) { + uint16 tid = SVAL(hdr, HDR_TID); + uint16 fnum = SVAL(vwv,VWV(2)); + uint8 level = CVAL(vwv,VWV(3)); + transport->oplock.handler(transport, tid, fnum, level, transport->oplock.private); + } + + return True; +} + + +/* + receive an async message from the server + this function assumes that the caller already knows that the socket is readable + and that there is a packet waiting + + The packet is not actually returned by this function, instead any + registered async message handlers are called + + return True if a packet was successfully received and processed + return False if the socket appears to be dead +*/ +BOOL cli_request_receive_next(struct cli_transport *transport) +{ + BOOL ret; + int len; + char header[NBT_HDR_SIZE]; + char *buffer, *hdr, *vwv; + TALLOC_CTX *mem_ctx; + struct cli_request *req; + uint16 wct, mid = 0; + + len = cli_sock_read(transport->socket, header, 4); + if (len != 4) { + return False; + } + + len = smb_len(header); + + mem_ctx = talloc_init("cli_request_receive_next"); + + /* allocate the incoming buffer at the right size */ + buffer = talloc(mem_ctx, len+NBT_HDR_SIZE); + if (!buffer) { + talloc_destroy(mem_ctx); + return False; + } + + /* fill in the already received header */ + memcpy(buffer, header, NBT_HDR_SIZE); + + ret = cli_sock_read(transport->socket, buffer + NBT_HDR_SIZE, len); + /* If the server is not responding, note that now */ + if (ret != len) { + return False; + } + + hdr = buffer+NBT_HDR_SIZE; + vwv = hdr + HDR_VWV; + + /* see if it could be an oplock break request */ + if (handle_oplock_break(transport, len, hdr, vwv)) { + goto done; + } + + /* at this point we need to check for a readbraw reply, as these can be any length */ + if (transport->readbraw_pending) { + transport->readbraw_pending = 0; + + /* it must match the first entry in the pending queue as the client is not allowed + to have outstanding readbraw requests */ + req = transport->pending_requests; + if (!req) goto done; + + req->in.buffer = buffer; + talloc_steal(mem_ctx, req->mem_ctx, buffer); + req->in.size = len + NBT_HDR_SIZE; + req->in.allocated = req->in.size; + goto async; + } + + if (len >= MIN_SMB_SIZE) { + /* extract the mid for matching to pending requests */ + mid = SVAL(hdr, HDR_MID); + wct = CVAL(hdr, HDR_WCT); + } + + /* match the incoming request against the list of pending requests */ + for (req=transport->pending_requests; req; req=req->next) { + if (req->mid == mid) break; + } + + if (!req) { + DEBUG(3,("Discarding unmatched reply with mid %d\n", mid)); + goto done; + } + + /* fill in the 'in' portion of the matching request */ + req->in.buffer = buffer; + talloc_steal(mem_ctx, req->mem_ctx, buffer); + req->in.size = len + NBT_HDR_SIZE; + req->in.allocated = req->in.size; + + /* handle non-SMB replies */ + if (req->in.size < NBT_HDR_SIZE + MIN_SMB_SIZE) { + goto done; + } + + if (req->in.size < NBT_HDR_SIZE + MIN_SMB_SIZE + VWV(wct)) { + DEBUG(2,("bad reply size for mid %d\n", mid)); + req->status = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + req->in.hdr = hdr; + req->in.vwv = vwv; + req->in.wct = wct; + if (req->in.size >= NBT_HDR_SIZE + MIN_SMB_SIZE + VWV(wct)) { + req->in.data = req->in.vwv + VWV(wct) + 2; + req->in.data_size = SVAL(req->in.vwv, VWV(wct)); + if (req->in.size < NBT_HDR_SIZE + MIN_SMB_SIZE + VWV(wct) + req->in.data_size) { + DEBUG(3,("bad data size for mid %d\n", mid)); + /* blergh - w2k3 gives a bogus data size values in some + openX replies */ + req->in.data_size = req->in.size - (NBT_HDR_SIZE + MIN_SMB_SIZE + VWV(wct)); + } + } + req->in.ptr = req->in.data; + req->flags2 = SVAL(req->in.hdr, HDR_FLG2); + + if (!(req->flags2 & FLAGS2_32_BIT_ERROR_CODES)) { + transport->error.etype = ETYPE_DOS; + transport->error.e.dos.eclass = CVAL(req->in.hdr,HDR_RCLS); + transport->error.e.dos.ecode = SVAL(req->in.hdr,HDR_ERR); + req->status = dos_to_ntstatus(transport->error.e.dos.eclass, + transport->error.e.dos.ecode); + } else { + transport->error.etype = ETYPE_NT; + transport->error.e.nt_status = NT_STATUS(IVAL(req->in.hdr, HDR_RCLS)); + req->status = transport->error.e.nt_status; + } + + if (!cli_request_check_sign_mac(req)) { + transport->error.etype = ETYPE_SOCKET; + transport->error.e.socket_error = SOCKET_READ_BAD_SIG; + return False; + }; + +async: + /* if this request has an async handler then call that to + notify that the reply has been received. This might destroy + the request so it must happen last */ + if (req->async.fn) { + req->async.fn(req); + } + +done: + talloc_destroy(mem_ctx); + return True; +} + + +/* + wait for a reply to be received for a packet that just returns an error + code and nothing more +*/ +NTSTATUS cli_request_simple_recv(struct cli_request *req) +{ + cli_request_receive(req); + return cli_request_destroy(req); +} + + +/* Return true if the last packet was in error */ +BOOL cli_request_is_error(struct cli_request *req) +{ + return NT_STATUS_IS_ERR(req->status); +} + +/* + append a string into the data portion of the request packet + + return the number of bytes added to the packet +*/ +size_t cli_req_append_string(struct cli_request *req, const char *str, unsigned flags) +{ + size_t len; + + /* determine string type to use */ + if (!(flags & (STR_ASCII|STR_UNICODE))) { + flags |= (req->transport->negotiate.capabilities & CAP_UNICODE) ? STR_UNICODE : STR_ASCII; + } + + len = (strlen(str)+2) * MAX_BYTES_PER_CHAR; + + cli_req_grow_allocation(req, len + req->out.data_size); + + len = push_string(NULL, req->out.data + req->out.data_size, str, len, flags); + + cli_req_grow_data(req, len + req->out.data_size); + + return len; +} + +/* + this is like cli_req_append_string but it also return the + non-terminated string byte length, which can be less than the number + of bytes consumed in the packet for 2 reasons: + + 1) the string in the packet may be null terminated + 2) the string in the packet may need a 1 byte UCS2 alignment + + this is used in places where the non-terminated string byte length is + placed in the packet as a separate field +*/ +size_t cli_req_append_string_len(struct cli_request *req, const char *str, unsigned flags, int *len) +{ + int diff = 0; + size_t ret; + + /* determine string type to use */ + if (!(flags & (STR_ASCII|STR_UNICODE))) { + flags |= (req->transport->negotiate.capabilities & CAP_UNICODE) ? STR_UNICODE : STR_ASCII; + } + + /* see if an alignment byte will be used */ + if ((flags & STR_UNICODE) && !(flags & STR_NOALIGN)) { + diff = ucs2_align(NULL, req->out.data + req->out.data_size, flags); + } + + /* do the hard work */ + ret = cli_req_append_string(req, str, flags); + + /* see if we need to subtract the termination */ + if (flags & STR_TERMINATE) { + diff += (flags & STR_UNICODE) ? 2 : 1; + } + + if (ret >= diff) { + (*len) = ret - diff; + } else { + (*len) = ret; + } + + return ret; +} + + +/* + push a string into the data portion of the request packet, growing it if necessary + this gets quite tricky - please be very careful to cover all cases when modifying this + + if dest is NULL, then put the string at the end of the data portion of the packet + + if dest_len is -1 then no limit applies +*/ +size_t cli_req_append_ascii4(struct cli_request *req, const char *str, unsigned flags) +{ + size_t size; + cli_req_append_bytes(req, (const uint8 *)"\4", 1); + size = cli_req_append_string(req, str, flags); + return size + 1; +} + + +/* + push a blob into the data portion of the request packet, growing it if necessary + this gets quite tricky - please be very careful to cover all cases when modifying this + + if dest is NULL, then put the blob at the end of the data portion of the packet +*/ +size_t cli_req_append_blob(struct cli_request *req, const DATA_BLOB *blob) +{ + cli_req_grow_allocation(req, req->out.data_size + blob->length); + memcpy(req->out.data + req->out.data_size, blob->data, blob->length); + cli_req_grow_data(req, req->out.data_size + blob->length); + return blob->length; +} + +/* + append raw bytes into the data portion of the request packet + return the number of bytes added +*/ +size_t cli_req_append_bytes(struct cli_request *req, const uint8 *bytes, size_t byte_len) +{ + cli_req_grow_allocation(req, byte_len + req->out.data_size); + memcpy(req->out.data + req->out.data_size, bytes, byte_len); + cli_req_grow_data(req, byte_len + req->out.data_size); + return byte_len; +} + +/* + append variable block (type 5 buffer) into the data portion of the request packet + return the number of bytes added +*/ +size_t cli_req_append_var_block(struct cli_request *req, const uint8 *bytes, uint16 byte_len) +{ + cli_req_grow_allocation(req, byte_len + 3 + req->out.data_size); + SCVAL(req->out.data + req->out.data_size, 0, 5); + SSVAL(req->out.data + req->out.data_size, 1, byte_len); /* add field length */ + if (byte_len > 0) { + memcpy(req->out.data + req->out.data_size + 3, bytes, byte_len); + } + cli_req_grow_data(req, byte_len + 3 + req->out.data_size); + return byte_len + 3; +} + + +/* + pull a UCS2 string from a request packet, returning a talloced unix string + + the string length is limited by the 3 things: + - the data size in the request (end of packet) + - the passed 'byte_len' if it is not -1 + - the end of string (null termination) + + Note that 'byte_len' is the number of bytes in the packet + + on failure zero is returned and *dest is set to NULL, otherwise the number + of bytes consumed in the packet is returned +*/ +static size_t cli_req_pull_ucs2(struct cli_request *req, TALLOC_CTX *mem_ctx, + char **dest, const char *src, int byte_len, unsigned flags) +{ + int src_len, src_len2, alignment=0; + ssize_t ret; + + if (!(flags & STR_NOALIGN) && ucs2_align(req->in.buffer, src, flags)) { + src++; + alignment=1; + if (byte_len != -1) { + byte_len--; + } + } + + src_len = req->in.data_size - PTR_DIFF(src, req->in.data); + if (src_len < 0) { + *dest = NULL; + return 0; + } + if (byte_len != -1 && src_len > byte_len) { + src_len = byte_len; + } + + src_len2 = strnlen_w((const smb_ucs2_t *)src, src_len/2) * 2; + if (src_len2 < src_len - 2) { + /* include the termination if we didn't reach the end of the packet */ + src_len2 += 2; + } + + /* ucs2 strings must be at least 2 bytes long */ + if (src_len2 < 2) { + *dest = NULL; + return 0; + } + + ret = convert_string_talloc(mem_ctx, CH_UCS2, CH_UNIX, src, src_len2, (const void **)dest); + if (ret == -1) { + *dest = NULL; + return 0; + } + + return src_len2 + alignment; +} + +/* + pull a ascii string from a request packet, returning a talloced string + + the string length is limited by the 3 things: + - the data size in the request (end of packet) + - the passed 'byte_len' if it is not -1 + - the end of string (null termination) + + Note that 'byte_len' is the number of bytes in the packet + + on failure zero is returned and *dest is set to NULL, otherwise the number + of bytes consumed in the packet is returned +*/ +size_t cli_req_pull_ascii(struct cli_request *req, TALLOC_CTX *mem_ctx, + char **dest, const char *src, int byte_len, unsigned flags) +{ + int src_len, src_len2; + ssize_t ret; + + src_len = req->in.data_size - PTR_DIFF(src, req->in.data); + if (src_len < 0) { + *dest = NULL; + return 0; + } + if (byte_len != -1 && src_len > byte_len) { + src_len = byte_len; + } + src_len2 = strnlen(src, src_len); + if (src_len2 < src_len - 1) { + /* include the termination if we didn't reach the end of the packet */ + src_len2++; + } + + ret = convert_string_talloc(mem_ctx, CH_DOS, CH_UNIX, src, src_len2, (const void **)dest); + + if (ret == -1) { + *dest = NULL; + return 0; + } + + return ret; +} + +/* + pull a string from a request packet, returning a talloced string + + the string length is limited by the 3 things: + - the data size in the request (end of packet) + - the passed 'byte_len' if it is not -1 + - the end of string (null termination) + + Note that 'byte_len' is the number of bytes in the packet + + on failure zero is returned and *dest is set to NULL, otherwise the number + of bytes consumed in the packet is returned +*/ +size_t cli_req_pull_string(struct cli_request *req, TALLOC_CTX *mem_ctx, + char **dest, const char *src, int byte_len, unsigned flags) +{ + if (!(flags & STR_ASCII) && + ((flags & STR_UNICODE || (req->flags2 & FLAGS2_UNICODE_STRINGS)))) { + return cli_req_pull_ucs2(req, mem_ctx, dest, src, byte_len, flags); + } + + return cli_req_pull_ascii(req, mem_ctx, dest, src, byte_len, flags); +} + + +/* + pull a DATA_BLOB from a reply packet, returning a talloced blob + make sure we don't go past end of packet + + if byte_len is -1 then limit the blob only by packet size +*/ +DATA_BLOB cli_req_pull_blob(struct cli_request *req, TALLOC_CTX *mem_ctx, const char *src, int byte_len) +{ + int src_len; + + src_len = req->in.data_size - PTR_DIFF(src, req->in.data); + + if (src_len < 0) { + return data_blob(NULL, 0); + } + + if (byte_len != -1 && src_len > byte_len) { + src_len = byte_len; + } + + return data_blob_talloc(mem_ctx, src, src_len); +} + +/* check that a lump of data in a request is within the bounds of the data section of + the packet */ +static BOOL cli_req_data_oob(struct cli_request *req, const char *ptr, uint32 count) +{ + /* be careful with wraparound! */ + if (ptr < req->in.data || + ptr >= req->in.data + req->in.data_size || + count > req->in.data_size || + ptr + count > req->in.data + req->in.data_size) { + return True; + } + return False; +} + +/* + pull a lump of data from a request packet + + return False if any part is outside the data portion of the packet +*/ +BOOL cli_raw_pull_data(struct cli_request *req, const char *src, int len, char *dest) +{ + if (len == 0) return True; + + if (cli_req_data_oob(req, src, len)) { + return False; + } + + memcpy(dest, src, len); + return True; +} + + +/* + put a NTTIME into a packet +*/ + +void cli_push_nttime(void *base, uint16 offset, NTTIME *t) +{ + SIVAL(base, offset, t->low); + SIVAL(base, offset+4, t->high); +} + +/* + pull a NTTIME from a packet +*/ +NTTIME cli_pull_nttime(void *base, uint16 offset) +{ + NTTIME ret; + ret.low = IVAL(base, offset); + ret.high = IVAL(base, offset+4); + return ret; +} + +/* + pull a UCS2 string from a blob, returning a talloced unix string + + the string length is limited by the 3 things: + - the data size in the blob + - the passed 'byte_len' if it is not -1 + - the end of string (null termination) + + Note that 'byte_len' is the number of bytes in the packet + + on failure zero is returned and *dest is set to NULL, otherwise the number + of bytes consumed in the blob is returned +*/ +static size_t cli_blob_pull_ucs2(TALLOC_CTX* mem_ctx, + DATA_BLOB *blob, const char **dest, + const char *src, int byte_len, unsigned flags) +{ + int src_len, src_len2, alignment=0; + ssize_t ret; + + if (src < (const char *)blob->data || + src >= (const char *)(blob->data + blob->length)) { + *dest = NULL; + return 0; + } + + src_len = blob->length - PTR_DIFF(src, blob->data); + + if (byte_len != -1 && src_len > byte_len) { + src_len = byte_len; + } + + if (!(flags & STR_NOALIGN) && ucs2_align(blob->data, src, flags)) { + src++; + alignment=1; + src_len--; + } + + if (src_len < 2) { + *dest = NULL; + return 0; + } + + src_len2 = strnlen_w((const smb_ucs2_t *)src, src_len/2) * 2; + + if (src_len2 < src_len - 2) { + /* include the termination if we didn't reach the end of the packet */ + src_len2 += 2; + } + + ret = convert_string_talloc(mem_ctx, CH_UCS2, CH_UNIX, src, src_len2, (const void **)dest); + if (ret == -1) { + *dest = NULL; + return 0; + } + + return src_len2 + alignment; +} + +/* + pull a ascii string from a blob, returning a talloced string + + the string length is limited by the 3 things: + - the data size in the blob + - the passed 'byte_len' if it is not -1 + - the end of string (null termination) + + Note that 'byte_len' is the number of bytes in the blob + + on failure zero is returned and *dest is set to NULL, otherwise the number + of bytes consumed in the blob is returned +*/ +static size_t cli_blob_pull_ascii(TALLOC_CTX *mem_ctx, + DATA_BLOB *blob, const char **dest, + const char *src, int byte_len, unsigned flags) +{ + int src_len, src_len2; + ssize_t ret; + + src_len = blob->length - PTR_DIFF(src, blob->data); + if (src_len < 0) { + *dest = NULL; + return 0; + } + if (byte_len != -1 && src_len > byte_len) { + src_len = byte_len; + } + src_len2 = strnlen(src, src_len); + + if (src_len2 < src_len - 1) { + /* include the termination if we didn't reach the end of the packet */ + src_len2++; + } + + ret = convert_string_talloc(mem_ctx, CH_DOS, CH_UNIX, src, src_len2, (const void **)dest); + + if (ret == -1) { + *dest = NULL; + return 0; + } + + return ret; +} + +/* + pull a string from a blob, returning a talloced WIRE_STRING + + the string length is limited by the 3 things: + - the data size in the blob + - length field on the wire + - the end of string (null termination) + + if STR_LEN8BIT is set in the flags then assume the length field is + 8 bits, instead of 32 + + on failure zero is returned and dest->s is set to NULL, otherwise the number + of bytes consumed in the blob is returned +*/ +size_t cli_blob_pull_string(struct cli_session *session, + TALLOC_CTX *mem_ctx, + DATA_BLOB *blob, + WIRE_STRING *dest, + uint16 len_offset, uint16 str_offset, + unsigned flags) +{ + dest->s = NULL; + + if (len_offset > blob->length-4) { + return 0; + } + if (flags & STR_LEN8BIT) { + dest->private_length = CVAL(blob->data, len_offset); + } else { + dest->private_length = IVAL(blob->data, len_offset); + } + dest->s = NULL; + if (!(flags & STR_ASCII) && + ((flags & STR_UNICODE) || + (session->transport->negotiate.capabilities & CAP_UNICODE))) { + if ((str_offset&1) && !(flags & STR_NOALIGN)) { + str_offset++; + } + return cli_blob_pull_ucs2(mem_ctx, blob, &dest->s, + blob->data+str_offset, dest->private_length, flags); + } + + return cli_blob_pull_ascii(mem_ctx, blob, &dest->s, + blob->data+str_offset, dest->private_length, flags); +} + +/* + append a string into a blob +*/ +size_t cli_blob_append_string(struct cli_session *session, + TALLOC_CTX *mem_ctx, DATA_BLOB *blob, + const char *str, unsigned flags) +{ + size_t max_len; + int len; + + if (!str) return 0; + + /* determine string type to use */ + if (!(flags & (STR_ASCII|STR_UNICODE))) { + flags |= (session->transport->negotiate.capabilities & CAP_UNICODE) ? STR_UNICODE : STR_ASCII; + } + + max_len = (strlen(str)+2) * MAX_BYTES_PER_CHAR; + + blob->data = talloc_realloc(mem_ctx, blob->data, blob->length + max_len); + if (!blob->data) { + return 0; + } + + len = push_string(NULL, blob->data + blob->length, str, max_len, flags); + + blob->length += len; + + return len; +} diff --git a/source4/libcli/raw/rawsearch.c b/source4/libcli/raw/rawsearch.c new file mode 100644 index 0000000000..bdc39bb68c --- /dev/null +++ b/source4/libcli/raw/rawsearch.c @@ -0,0 +1,569 @@ +/* + Unix SMB/CIFS implementation. + client directory search routines + Copyright (C) James Myers 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +/**************************************************************************** + Old style search backend - process output. +****************************************************************************/ +static void smb_raw_search_backend(struct cli_request *req, + TALLOC_CTX *mem_ctx, + uint16 count, + void *private, + BOOL (*callback)(void *private, union smb_search_data *file)) + +{ + union smb_search_data search_data; + int i; + char *p; + + if (req->in.data_size < 3 + count*43) { + req->status = NT_STATUS_INVALID_PARAMETER; + return; + } + + p = req->in.data + 3; + + for (i=0; i < count; i++) { + search_data.search.search_id = cli_req_pull_blob(req, mem_ctx, p, 21); + search_data.search.attrib = CVAL(p, 21); + search_data.search.write_time = make_unix_date(p + 22); + search_data.search.size = IVAL(p, 26); + cli_req_pull_ascii(req, mem_ctx, &search_data.search.name, p+30, 13, STR_ASCII); + if (!callback(private, &search_data)) { + break; + } + p += 43; + } +} + +/**************************************************************************** + Old style search first. +****************************************************************************/ +static NTSTATUS smb_raw_search_first_old(struct cli_tree *tree, + TALLOC_CTX *mem_ctx, + union smb_search_first *io, void *private, + BOOL (*callback)(void *private, union smb_search_data *file)) + +{ + struct cli_request *req; + + req = cli_request_setup(tree, SMBsearch, 2, 0); + if (!req) { + return NT_STATUS_NO_MEMORY; + } + + SSVAL(req->out.vwv, VWV(0), io->search_first.in.max_count); + SSVAL(req->out.vwv, VWV(1), io->search_first.in.search_attrib); + cli_req_append_ascii4(req, io->search_first.in.pattern, STR_TERMINATE); + cli_req_append_var_block(req, NULL, 0); + + if (!cli_request_send(req) || + !cli_request_receive(req)) { + return cli_request_destroy(req); + } + + if (NT_STATUS_IS_OK(req->status)) { + io->search_first.out.count = SVAL(req->in.vwv, VWV(0)); + smb_raw_search_backend(req, mem_ctx, io->search_first.out.count, private, callback); + } + + return cli_request_destroy(req); +} + +/**************************************************************************** + Old style search next. +****************************************************************************/ +static NTSTATUS smb_raw_search_next_old(struct cli_tree *tree, + TALLOC_CTX *mem_ctx, + union smb_search_next *io, void *private, + BOOL (*callback)(void *private, union smb_search_data *file)) + +{ + struct cli_request *req; + + req = cli_request_setup(tree, SMBsearch, 2, 0); + if (!req) { + return NT_STATUS_NO_MEMORY; + } + + SSVAL(req->out.vwv, VWV(0), io->search_next.in.max_count); + SSVAL(req->out.vwv, VWV(1), io->search_next.in.search_attrib); + cli_req_append_ascii4(req, "", STR_TERMINATE); + cli_req_append_var_block(req, io->search_next.in.search_id.data, 21); + + if (!cli_request_send(req) || + !cli_request_receive(req)) { + return cli_request_destroy(req); + } + + if (NT_STATUS_IS_OK(req->status)) { + io->search_next.out.count = SVAL(req->in.vwv, VWV(0)); + smb_raw_search_backend(req, mem_ctx, io->search_next.out.count, private, callback); + } + + return cli_request_destroy(req); +} + +/**************************************************************************** + Very raw search first - returns param/data blobs. +****************************************************************************/ +static NTSTATUS smb_raw_search_first_blob(struct cli_tree *tree, + TALLOC_CTX *mem_ctx, /* used to allocate output blobs */ + union smb_search_first *io, + uint16 info_level, + DATA_BLOB *out_param_blob, + DATA_BLOB *out_data_blob) +{ + struct smb_trans2 tp; + uint16 setup = TRANSACT2_FINDFIRST; + NTSTATUS status; + + tp.in.max_setup = 0; + tp.in.flags = 0; + tp.in.timeout = 0; + tp.in.setup_count = 1; + tp.in.data = data_blob(NULL, 0); + tp.in.max_param = 1024; + tp.in.max_data = 8192; + tp.in.setup = &setup; + + tp.in.params = data_blob_talloc(mem_ctx, NULL, 12); + if (!tp.in.params.data) { + return NT_STATUS_NO_MEMORY; + } + + SSVAL(tp.in.params.data, 0, io->t2ffirst.in.search_attrib); + SSVAL(tp.in.params.data, 2, io->t2ffirst.in.max_count); + SSVAL(tp.in.params.data, 4, io->t2ffirst.in.flags); + SSVAL(tp.in.params.data, 6, info_level); + SIVAL(tp.in.params.data, 8, io->t2ffirst.in.storage_type); + + cli_blob_append_string(tree->session, mem_ctx, &tp.in.params, + io->t2ffirst.in.pattern, STR_TERMINATE); + + status = smb_raw_trans2(tree, mem_ctx, &tp); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + out_param_blob->length = tp.out.params.length; + out_param_blob->data = tp.out.params.data; + out_data_blob->length = tp.out.data.length; + out_data_blob->data = tp.out.data.data; + + return NT_STATUS_OK; +} + + +/**************************************************************************** + Very raw search first - returns param/data blobs. + Used in CIFS-on-CIFS NTVFS. +****************************************************************************/ +static NTSTATUS smb_raw_search_next_blob(struct cli_tree *tree, + TALLOC_CTX *mem_ctx, + union smb_search_next *io, + uint16 info_level, + DATA_BLOB *out_param_blob, + DATA_BLOB *out_data_blob) +{ + struct smb_trans2 tp; + uint16 setup = TRANSACT2_FINDNEXT; + NTSTATUS status; + + tp.in.max_setup = 0; + tp.in.flags = 0; + tp.in.timeout = 0; + tp.in.setup_count = 1; + tp.in.data = data_blob(NULL, 0); + tp.in.max_param = 1024; + tp.in.max_data = 8192; + tp.in.setup = &setup; + + tp.in.params = data_blob_talloc(mem_ctx, NULL, 12); + if (!tp.in.params.data) { + return NT_STATUS_NO_MEMORY; + } + + SSVAL(tp.in.params.data, 0, io->t2fnext.in.handle); + SSVAL(tp.in.params.data, 2, io->t2fnext.in.max_count); + SSVAL(tp.in.params.data, 4, info_level); + SIVAL(tp.in.params.data, 6, io->t2fnext.in.resume_key); + SSVAL(tp.in.params.data, 10, io->t2fnext.in.flags); + + cli_blob_append_string(tree->session, mem_ctx, &tp.in.params, + io->t2fnext.in.last_name, + STR_TERMINATE); + + status = smb_raw_trans2(tree, mem_ctx, &tp); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + out_param_blob->length = tp.out.params.length; + out_param_blob->data = tp.out.params.data; + out_data_blob->length = tp.out.data.length; + out_data_blob->data = tp.out.data.data; + + return NT_STATUS_OK; +} + + +/* + parse a trans2 search response. + Return the number of bytes consumed + return 0 for success with end of list + return -1 for a parse error +*/ +static int parse_trans2_search(struct cli_tree *tree, + TALLOC_CTX *mem_ctx, + enum search_level level, + uint16 flags, + DATA_BLOB *blob, + union smb_search_data *data) +{ + uint_t len, ofs; + + switch (level) { + case RAW_SEARCH_GENERIC: + case RAW_SEARCH_SEARCH: + /* handled elsewhere */ + return -1; + + case RAW_SEARCH_STANDARD: + if (flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) { + if (blob->length < 4) return -1; + data->standard.resume_key = IVAL(blob->data, 0); + blob->data += 4; + blob->length -= 4; + } + if (blob->length < 24) return -1; + data->standard.create_time = make_unix_date2(blob->data + 0); + data->standard.access_time = make_unix_date2(blob->data + 4); + data->standard.write_time = make_unix_date2(blob->data + 8); + data->standard.size = IVAL(blob->data, 12); + data->standard.alloc_size = IVAL(blob->data, 16); + data->standard.attrib = SVAL(blob->data, 20); + len = cli_blob_pull_string(tree->session, mem_ctx, blob, + &data->standard.name, + 22, 23, STR_LEN8BIT); + return (len + 23 + 3) & ~3; + + case RAW_SEARCH_EA_SIZE: + if (flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) { + if (blob->length < 4) return -1; + data->ea_size.resume_key = IVAL(blob->data, 0); + blob->data += 4; + blob->length -= 4; + } + if (blob->length < 28) return -1; + data->ea_size.create_time = make_unix_date2(blob->data + 0); + data->ea_size.access_time = make_unix_date2(blob->data + 4); + data->ea_size.write_time = make_unix_date2(blob->data + 8); + data->ea_size.size = IVAL(blob->data, 12); + data->ea_size.alloc_size = IVAL(blob->data, 16); + data->ea_size.attrib = SVAL(blob->data, 20); + data->ea_size.ea_size = IVAL(blob->data, 22); + len = cli_blob_pull_string(tree->session, mem_ctx, blob, + &data->ea_size.name, + 26, 27, STR_LEN8BIT | STR_NOALIGN); + return len + 27; + + case RAW_SEARCH_DIRECTORY_INFO: + if (blob->length < 65) return -1; + ofs = IVAL(blob->data, 0); + data->directory_info.file_index = IVAL(blob->data, 4); + data->directory_info.create_time = cli_pull_nttime(blob->data, 8); + data->directory_info.access_time = cli_pull_nttime(blob->data, 16); + data->directory_info.write_time = cli_pull_nttime(blob->data, 24); + data->directory_info.change_time = cli_pull_nttime(blob->data, 32); + data->directory_info.size = BVAL(blob->data, 40); + data->directory_info.alloc_size = BVAL(blob->data, 48); + data->directory_info.attrib = IVAL(blob->data, 56); + len = cli_blob_pull_string(tree->session, mem_ctx, blob, + &data->directory_info.name, + 60, 64, 0); + if (ofs != 0 && ofs < 64+len) { + return -1; + } + return ofs; + + case RAW_SEARCH_FULL_DIRECTORY_INFO: + if (blob->length < 69) return -1; + ofs = IVAL(blob->data, 0); + data->full_directory_info.file_index = IVAL(blob->data, 4); + data->full_directory_info.create_time = cli_pull_nttime(blob->data, 8); + data->full_directory_info.access_time = cli_pull_nttime(blob->data, 16); + data->full_directory_info.write_time = cli_pull_nttime(blob->data, 24); + data->full_directory_info.change_time = cli_pull_nttime(blob->data, 32); + data->full_directory_info.size = BVAL(blob->data, 40); + data->full_directory_info.alloc_size = BVAL(blob->data, 48); + data->full_directory_info.attrib = IVAL(blob->data, 56); + data->full_directory_info.ea_size = IVAL(blob->data, 64); + len = cli_blob_pull_string(tree->session, mem_ctx, blob, + &data->full_directory_info.name, + 60, 68, 0); + if (ofs != 0 && ofs < 68+len) { + return -1; + } + return ofs; + + case RAW_SEARCH_NAME_INFO: + if (blob->length < 13) return -1; + ofs = IVAL(blob->data, 0); + data->name_info.file_index = IVAL(blob->data, 4); + len = cli_blob_pull_string(tree->session, mem_ctx, blob, + &data->name_info.name, + 8, 12, 0); + if (ofs != 0 && ofs < 12+len) { + return -1; + } + return ofs; + + + case RAW_SEARCH_BOTH_DIRECTORY_INFO: + if (blob->length < 95) return -1; + ofs = IVAL(blob->data, 0); + data->both_directory_info.file_index = IVAL(blob->data, 4); + data->both_directory_info.create_time = cli_pull_nttime(blob->data, 8); + data->both_directory_info.access_time = cli_pull_nttime(blob->data, 16); + data->both_directory_info.write_time = cli_pull_nttime(blob->data, 24); + data->both_directory_info.change_time = cli_pull_nttime(blob->data, 32); + data->both_directory_info.size = BVAL(blob->data, 40); + data->both_directory_info.alloc_size = BVAL(blob->data, 48); + data->both_directory_info.attrib = IVAL(blob->data, 56); + data->both_directory_info.ea_size = IVAL(blob->data, 64); + cli_blob_pull_string(tree->session, mem_ctx, blob, + &data->both_directory_info.short_name, + 68, 70, STR_LEN8BIT | STR_UNICODE); + len = cli_blob_pull_string(tree->session, mem_ctx, blob, + &data->both_directory_info.name, + 60, 94, 0); + if (ofs != 0 && ofs < 94+len) { + return -1; + } + return ofs; + + + case RAW_SEARCH_261: + if (blob->length < 81) return -1; + ofs = IVAL(blob->data, 0); + data->level_261.file_index = IVAL(blob->data, 4); + data->level_261.create_time = cli_pull_nttime(blob->data, 8); + data->level_261.access_time = cli_pull_nttime(blob->data, 16); + data->level_261.write_time = cli_pull_nttime(blob->data, 24); + data->level_261.change_time = cli_pull_nttime(blob->data, 32); + data->level_261.size = BVAL(blob->data, 40); + data->level_261.alloc_size = BVAL(blob->data, 48); + data->level_261.attrib = IVAL(blob->data, 56); + data->level_261.ea_size = IVAL(blob->data, 64); + data->level_261.unknown[0] = IVAL(blob->data, 68); + data->level_261.unknown[1] = IVAL(blob->data, 72); + data->level_261.unknown[2] = IVAL(blob->data, 76); + len = cli_blob_pull_string(tree->session, mem_ctx, blob, + &data->level_261.name, + 60, 80, 0); + if (ofs != 0 && ofs < 80+len) { + return -1; + } + return ofs; + + case RAW_SEARCH_262: + if (blob->length < 105) return -1; + ofs = IVAL(blob->data, 0); + data->level_262.file_index = IVAL(blob->data, 4); + data->level_262.create_time = cli_pull_nttime(blob->data, 8); + data->level_262.access_time = cli_pull_nttime(blob->data, 16); + data->level_262.write_time = cli_pull_nttime(blob->data, 24); + data->level_262.change_time = cli_pull_nttime(blob->data, 32); + data->level_262.size = BVAL(blob->data, 40); + data->level_262.alloc_size = BVAL(blob->data, 48); + data->level_262.attrib = SVAL(blob->data, 56); + data->level_262.ea_size = IVAL(blob->data, 64); + cli_blob_pull_string(tree->session, mem_ctx, blob, + &data->level_262.short_name, + 68, 70, STR_LEN8BIT | STR_UNICODE); + data->level_262.unknown[0] = IVAL(blob->data, 94); + data->level_262.unknown[1] = IVAL(blob->data, 98); + len = cli_blob_pull_string(tree->session, mem_ctx, blob, + &data->level_262.name, + 60, 104, 0); + if (ofs != 0 && ofs < 104+len) { + return -1; + } + return ofs; + } + + /* invalid level */ + return -1; +} + +/**************************************************************************** + Trans2 search backend - process output. +****************************************************************************/ +static NTSTATUS smb_raw_t2search_backend(struct cli_tree *tree, + TALLOC_CTX *mem_ctx, + enum search_level level, + uint16 flags, + int16 count, + DATA_BLOB *blob, + void *private, + BOOL (*callback)(void *private, union smb_search_data *file)) + +{ + int i; + DATA_BLOB blob2; + + blob2.data = blob->data; + blob2.length = blob->length; + + for (i=0; i < count; i++) { + union smb_search_data search_data; + uint_t len; + + len = parse_trans2_search(tree, mem_ctx, level, flags, &blob2, &search_data); + if (len == -1) { + return NT_STATUS_INVALID_PARAMETER; + } + + /* the callback function can tell us that no more will + fit - in that case we stop, but it isn't an error */ + if (!callback(private, &search_data)) { + break; + } + + if (len == 0) break; + + blob2.data += len; + blob2.length -= len; + } + + return NT_STATUS_OK; +} + + +/* Implements trans2findfirst2 and old search + */ +NTSTATUS smb_raw_search_first(struct cli_tree *tree, + TALLOC_CTX *mem_ctx, + union smb_search_first *io, void *private, + BOOL (*callback)(void *private, union smb_search_data *file)) +{ + uint16 info_level = 0; + DATA_BLOB p_blob, d_blob; + NTSTATUS status; + + if (io->generic.level == RAW_SEARCH_SEARCH) { + return smb_raw_search_first_old(tree, mem_ctx, io, private, callback); + } + if (io->generic.level >= RAW_SEARCH_GENERIC) { + return NT_STATUS_INVALID_LEVEL; + } + info_level = (uint16)io->generic.level; + + status = smb_raw_search_first_blob(tree, mem_ctx, + io, info_level, &p_blob, &d_blob); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (p_blob.length != 10) { + DEBUG(1,("smb_raw_search_first: parms wrong size %d != expected_param_size\n", + p_blob.length)); + return NT_STATUS_INVALID_PARAMETER; + } + + /* process output data */ + io->t2ffirst.out.handle = SVAL(p_blob.data, 0); + io->t2ffirst.out.count = SVAL(p_blob.data, 2); + io->t2ffirst.out.end_of_search = SVAL(p_blob.data, 4); + + status = smb_raw_t2search_backend(tree, mem_ctx, + io->generic.level, + io->t2ffirst.in.flags, io->t2ffirst.out.count, + &d_blob, private, callback); + + return status; +} + +/* Implements trans2findnext2 and old smbsearch + */ +NTSTATUS smb_raw_search_next(struct cli_tree *tree, + TALLOC_CTX *mem_ctx, + union smb_search_next *io, void *private, + BOOL (*callback)(void *private, union smb_search_data *file)) +{ + uint16 info_level = 0; + DATA_BLOB p_blob, d_blob; + NTSTATUS status; + + if (io->generic.level == RAW_SEARCH_SEARCH) { + return smb_raw_search_next_old(tree, mem_ctx, io, private, callback); + } + if (io->generic.level >= RAW_SEARCH_GENERIC) { + return NT_STATUS_INVALID_LEVEL; + } + info_level = (uint16)io->generic.level; + + status = smb_raw_search_next_blob(tree, mem_ctx, + io, info_level, &p_blob, &d_blob); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (p_blob.length != 8) { + DEBUG(1,("smb_raw_search_next: parms wrong size %d != expected_param_size\n", + p_blob.length)); + return NT_STATUS_INVALID_PARAMETER; + } + + /* process output data */ + io->t2fnext.out.count = SVAL(p_blob.data, 0); + io->t2fnext.out.end_of_search = SVAL(p_blob.data, 2); + + status = smb_raw_t2search_backend(tree, mem_ctx, + io->generic.level, + io->t2fnext.in.flags, io->t2fnext.out.count, + &d_blob, private, callback); + + return status; +} + +/* + Implements trans2findclose2 + */ +NTSTATUS smb_raw_search_close(struct cli_tree *tree, + union smb_search_close *io) +{ + struct cli_request *req; + + req = cli_request_setup(tree, SMBfindclose, 1, 0); + if (!req) { + return NT_STATUS_NO_MEMORY; + } + + SSVAL(req->out.vwv, VWV(0), io->findclose.in.handle); + + if (cli_request_send(req)) { + cli_request_receive(req); + } + + return cli_request_destroy(req); +} diff --git a/source4/libcli/raw/rawsetfileinfo.c b/source4/libcli/raw/rawsetfileinfo.c new file mode 100644 index 0000000000..4044686c64 --- /dev/null +++ b/source4/libcli/raw/rawsetfileinfo.c @@ -0,0 +1,335 @@ +/* + Unix SMB/CIFS implementation. + RAW_SFILEINFO_* calls + Copyright (C) James Myers 2003 + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 qfileinfo/qpathinfo trans2 backend. +****************************************************************************/ +static BOOL smb_raw_setinfo_backend(struct cli_tree *tree, + TALLOC_CTX *mem_ctx, + union smb_setfileinfo *parms, + DATA_BLOB *blob) +{ + uint_t len; + +#define NEED_BLOB(n) do { \ + *blob = data_blob_talloc(mem_ctx, NULL, n); \ + if (blob->data == NULL) return False; \ + } while (0) + + switch (parms->generic.level) { + case RAW_SFILEINFO_GENERIC: + case RAW_SFILEINFO_SETATTR: + case RAW_SFILEINFO_SETATTRE: + /* not handled here */ + return False; + + case RAW_SFILEINFO_STANDARD: + NEED_BLOB(12); + put_dos_date2(blob->data, 0, parms->standard.in.create_time); + put_dos_date2(blob->data, 4, parms->standard.in.access_time); + put_dos_date2(blob->data, 8, parms->standard.in.write_time); + return True; + + case RAW_SFILEINFO_EA_SET: + NEED_BLOB(ea_list_size(1, &parms->ea_set.in.ea)); + ea_put_list(blob->data, 1, &parms->ea_set.in.ea); + return True; + + case RAW_SFILEINFO_BASIC_INFO: + case RAW_SFILEINFO_BASIC_INFORMATION: + NEED_BLOB(40); + cli_push_nttime(blob->data, 0, &parms->basic_info.in.create_time); + cli_push_nttime(blob->data, 8, &parms->basic_info.in.access_time); + cli_push_nttime(blob->data, 16, &parms->basic_info.in.write_time); + cli_push_nttime(blob->data, 24, &parms->basic_info.in.change_time); + SIVAL(blob->data, 32, parms->basic_info.in.attrib); + SIVAL(blob->data, 36, 0); /* padding */ + return True; + + case RAW_SFILEINFO_UNIX_BASIC: + NEED_BLOB(92); + SBVAL(blob->data, 0, parms->unix_basic.in.end_of_file); + SBVAL(blob->data, 8, parms->unix_basic.in.num_bytes); + cli_push_nttime(blob->data, 16, &parms->unix_basic.in.status_change_time); + cli_push_nttime(blob->data, 24, &parms->unix_basic.in.access_time); + cli_push_nttime(blob->data, 32, &parms->unix_basic.in.change_time); + SBVAL(blob->data, 40, parms->unix_basic.in.uid); + SBVAL(blob->data, 48, parms->unix_basic.in.gid); + SIVAL(blob->data, 56, parms->unix_basic.in.file_type); + SBVAL(blob->data, 60, parms->unix_basic.in.dev_major); + SBVAL(blob->data, 68, parms->unix_basic.in.dev_minor); + SBVAL(blob->data, 76, parms->unix_basic.in.unique_id); + SBVAL(blob->data, 84, parms->unix_basic.in.nlink); + return True; + + case RAW_SFILEINFO_DISPOSITION_INFO: + case RAW_SFILEINFO_DISPOSITION_INFORMATION: + NEED_BLOB(4); + SIVAL(blob->data, 0, parms->disposition_info.in.delete_on_close); + return True; + + case RAW_SFILEINFO_ALLOCATION_INFO: + case RAW_SFILEINFO_ALLOCATION_INFORMATION: + NEED_BLOB(8); + SBVAL(blob->data, 0, parms->allocation_info.in.alloc_size); + return True; + + case RAW_SFILEINFO_END_OF_FILE_INFO: + case RAW_SFILEINFO_END_OF_FILE_INFORMATION: + NEED_BLOB(8); + SBVAL(blob->data, 0, parms->end_of_file_info.in.size); + return True; + + case RAW_SFILEINFO_RENAME_INFORMATION: + NEED_BLOB(12); + SIVAL(blob->data, 0, parms->rename_information.in.overwrite); + SIVAL(blob->data, 4, parms->rename_information.in.root_fid); + len = cli_blob_append_string(tree->session, mem_ctx, blob, + parms->rename_information.in.new_name, + STR_UNICODE|STR_TERMINATE); + SIVAL(blob->data, 8, len - 2); + return True; + + case RAW_SFILEINFO_POSITION_INFORMATION: + NEED_BLOB(8); + SBVAL(blob->data, 0, parms->position_information.in.position); + return True; + + case RAW_SFILEINFO_MODE_INFORMATION: + NEED_BLOB(4); + SIVAL(blob->data, 0, parms->mode_information.in.mode); + return True; + } + + return False; +} + +/**************************************************************************** + Very raw set file info - takes data blob (async send) +****************************************************************************/ +static struct cli_request *smb_raw_setfileinfo_blob_send(struct cli_tree *tree, + TALLOC_CTX *mem_ctx, + uint16 fnum, + uint16 info_level, + DATA_BLOB *blob) +{ + struct smb_trans2 tp; + uint16 setup = TRANSACT2_SETFILEINFO; + + tp.in.max_setup = 0; + tp.in.flags = 0; + tp.in.timeout = 0; + tp.in.setup_count = 1; + tp.in.max_param = 2; + tp.in.max_data = 0; + tp.in.setup = &setup; + + tp.in.params = data_blob_talloc(mem_ctx, NULL, 6); + if (!tp.in.params.data) { + return NULL; + } + SSVAL(tp.in.params.data, 0, fnum); + SSVAL(tp.in.params.data, 2, info_level); + SSVAL(tp.in.params.data, 4, 0); /* reserved */ + + tp.in.data = *blob; + + return smb_raw_trans2_send(tree, &tp); +} + +/**************************************************************************** + Very raw set path info - takes data blob +****************************************************************************/ +static struct cli_request *smb_raw_setpathinfo_blob_send(struct cli_tree *tree, + TALLOC_CTX *mem_ctx, + const char *fname, + uint16 info_level, + DATA_BLOB *blob) +{ + struct smb_trans2 tp; + uint16 setup = TRANSACT2_SETPATHINFO; + + tp.in.max_setup = 0; + tp.in.flags = 0; + tp.in.timeout = 0; + tp.in.setup_count = 1; + tp.in.max_param = 2; + tp.in.max_data = 0; + tp.in.setup = &setup; + + tp.in.params = data_blob_talloc(mem_ctx, NULL, 4); + if (!tp.in.params.data) { + return NULL; + } + SSVAL(tp.in.params.data, 0, info_level); + SSVAL(tp.in.params.data, 2, 0); + cli_blob_append_string(tree->session, mem_ctx, + &tp.in.params, + fname, STR_TERMINATE); + + tp.in.data = *blob; + + return smb_raw_trans2_send(tree, &tp); +} + +/**************************************************************************** + Handle setattr (async send) +****************************************************************************/ +static struct cli_request *smb_raw_setattr_send(struct cli_tree *tree, + union smb_setfileinfo *parms) +{ + struct cli_request *req; + + req = cli_request_setup(tree, SMBsetatr, 8, 0); + if (!req) return NULL; + + SSVAL(req->out.vwv, VWV(0), parms->setattr.in.attrib); + put_dos_date3(req->out.vwv, VWV(1), parms->setattr.in.write_time); + memset(req->out.vwv + VWV(3), 0, 10); /* reserved */ + cli_req_append_ascii4(req, parms->setattr.file.fname, STR_TERMINATE); + cli_req_append_ascii4(req, "", STR_TERMINATE); + + if (!cli_request_send(req)) { + cli_request_destroy(req); + return NULL; + } + + return req; +} + +/**************************************************************************** + Handle setattrE. (async send) +****************************************************************************/ +static struct cli_request *smb_raw_setattrE_send(struct cli_tree *tree, + union smb_setfileinfo *parms) +{ + struct cli_request *req; + + req = cli_request_setup(tree, SMBsetattrE, 7, 0); + if (!req) return NULL; + + SSVAL(req->out.vwv, VWV(0), parms->setattre.file.fnum); + put_dos_date2(req->out.vwv, VWV(1), parms->setattre.in.create_time); + put_dos_date2(req->out.vwv, VWV(3), parms->setattre.in.access_time); + put_dos_date2(req->out.vwv, VWV(5), parms->setattre.in.write_time); + + if (!cli_request_send(req)) { + cli_request_destroy(req); + return NULL; + } + + return req; +} + +/**************************************************************************** + Set file info (async send) +****************************************************************************/ +struct cli_request *smb_raw_setfileinfo_send(struct cli_tree *tree, + union smb_setfileinfo *parms) +{ + DATA_BLOB blob; + TALLOC_CTX *mem_ctx; + struct cli_request *req; + + if (parms->generic.level == RAW_SFILEINFO_SETATTRE) { + return smb_raw_setattrE_send(tree, parms); + } + if (parms->generic.level >= RAW_SFILEINFO_GENERIC) { + return NULL; + } + + mem_ctx = talloc_init("setpathinfo"); + if (!mem_ctx) return NULL; + + if (!smb_raw_setinfo_backend(tree, mem_ctx, parms, &blob)) { + talloc_destroy(mem_ctx); + return NULL; + } + + /* send request and process the output */ + req = smb_raw_setfileinfo_blob_send(tree, + mem_ctx, + parms->generic.file.fnum, + parms->generic.level, + &blob); + + talloc_destroy(mem_ctx); + return req; +} + +/**************************************************************************** + Set file info (async send) +****************************************************************************/ +NTSTATUS smb_raw_setfileinfo(struct cli_tree *tree, + union smb_setfileinfo *parms) +{ + struct cli_request *req = smb_raw_setfileinfo_send(tree, parms); + return cli_request_simple_recv(req); +} + + +/**************************************************************************** + Set path info (async send) +****************************************************************************/ +struct cli_request *smb_raw_setpathinfo_send(struct cli_tree *tree, + union smb_setfileinfo *parms) +{ + DATA_BLOB blob; + TALLOC_CTX *mem_ctx; + struct cli_request *req; + + if (parms->generic.level == RAW_SFILEINFO_SETATTR) { + return smb_raw_setattr_send(tree, parms); + } + if (parms->generic.level >= RAW_SFILEINFO_GENERIC) { + return NULL; + } + + mem_ctx = talloc_init("setpathinfo"); + if (!mem_ctx) return NULL; + + if (!smb_raw_setinfo_backend(tree, mem_ctx, parms, &blob)) { + talloc_destroy(mem_ctx); + return NULL; + } + + /* send request and process the output */ + req = smb_raw_setpathinfo_blob_send(tree, + mem_ctx, + parms->generic.file.fname, + parms->generic.level, + &blob); + + talloc_destroy(mem_ctx); + return req; +} + +/**************************************************************************** + Set path info (sync interface) +****************************************************************************/ +NTSTATUS smb_raw_setpathinfo(struct cli_tree *tree, + union smb_setfileinfo *parms) +{ + struct cli_request *req = smb_raw_setpathinfo_send(tree, parms); + return cli_request_simple_recv(req); +} diff --git a/source4/libcli/raw/rawtrans.c b/source4/libcli/raw/rawtrans.c new file mode 100644 index 0000000000..508f920268 --- /dev/null +++ b/source4/libcli/raw/rawtrans.c @@ -0,0 +1,489 @@ +/* + Unix SMB/CIFS implementation. + raw trans/trans2/nttrans operations + + Copyright (C) James Myers 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 out of bounds for incoming data +*/ +static BOOL raw_trans_oob(struct cli_request *req, + uint_t offset, uint_t count) +{ + char *ptr; + + if (count == 0) { + return False; + } + + ptr = req->in.hdr + offset; + + /* be careful with wraparound! */ + if (ptr < req->in.data || + ptr >= req->in.data + req->in.data_size || + count > req->in.data_size || + ptr + count > req->in.data + req->in.data_size) { + return True; + } + return False; +} + +/**************************************************************************** + receive a SMB trans or trans2 response allocating the necessary memory + ****************************************************************************/ +NTSTATUS smb_raw_trans2_recv(struct cli_request *req, + TALLOC_CTX *mem_ctx, + struct smb_trans2 *parms) +{ + int total_data=0; + int total_param=0; + char *tdata; + char *tparam; + + parms->out.data.length = 0; + parms->out.data.data = NULL; + parms->out.params.length = 0; + parms->out.params.data = NULL; + + if (!cli_request_receive(req)) { + req->status = NT_STATUS_UNSUCCESSFUL; + return cli_request_destroy(req); + } + + /* + * 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 (NT_STATUS_IS_ERR(req->status)) { + return cli_request_destroy(req); + } + + CLI_CHECK_MIN_WCT(req, 10); + + /* parse out the lengths */ + total_data = SVAL(req->in.vwv, VWV(1)); + total_param = SVAL(req->in.vwv, VWV(0)); + + /* allocate it */ + if (total_data != 0) { + tdata = talloc_realloc(mem_ctx, parms->out.data.data,total_data); + if (!tdata) { + DEBUG(0,("smb_raw_receive_trans: failed to enlarge data buffer to %d bytes\n", total_data)); + req->status = NT_STATUS_NO_MEMORY; + return cli_request_destroy(req); + } + parms->out.data.data = tdata; + } + + if (total_param != 0) { + tparam = talloc_realloc(mem_ctx, parms->out.params.data,total_param); + if (!tparam) { + DEBUG(0,("smb_raw_receive_trans: failed to enlarge param buffer to %d bytes\n", total_param)); + req->status = NT_STATUS_NO_MEMORY; + return cli_request_destroy(req); + } + parms->out.params.data = tparam; + } + + parms->out.setup_count = SVAL(req->in.vwv, VWV(9)); + CLI_CHECK_WCT(req, 10 + parms->out.setup_count); + + if (parms->out.setup_count > 0) { + int i; + parms->out.setup = talloc(mem_ctx, 2 * parms->out.setup_count); + if (!parms->out.setup) { + req->status = NT_STATUS_NO_MEMORY; + return cli_request_destroy(req); + } + for (i=0;iout.setup_count;i++) { + parms->out.setup[i] = SVAL(req->in.vwv, VWV(10+i)); + } + } + + while (1) { + uint16 param_count, param_ofs, param_disp; + uint16 data_count, data_ofs, data_disp; + uint16 total_data2, total_param2; + + /* parse out the total lengths again - they can shrink! */ + total_data2 = SVAL(req->in.vwv, VWV(1)); + total_param2 = SVAL(req->in.vwv, VWV(0)); + + if (total_data2 > total_data || + total_param2 > total_param) { + /* they must *only* shrink */ + DEBUG(1,("smb_raw_receive_trans: data/params expanded!\n")); + req->status = NT_STATUS_BUFFER_TOO_SMALL; + return cli_request_destroy(req); + } + + total_data = total_data2; + total_param = total_param2; + + /* parse params for this lump */ + param_count = SVAL(req->in.vwv, VWV(3)); + param_ofs = SVAL(req->in.vwv, VWV(4)); + param_disp = SVAL(req->in.vwv, VWV(5)); + + data_count = SVAL(req->in.vwv, VWV(6)); + data_ofs = SVAL(req->in.vwv, VWV(7)); + data_disp = SVAL(req->in.vwv, VWV(8)); + + if (data_count + data_disp > total_data || + param_count + param_disp > total_param) { + DEBUG(1,("smb_raw_receive_trans: Buffer overflow\n")); + req->status = NT_STATUS_BUFFER_TOO_SMALL; + return cli_request_destroy(req); + } + + /* check the server isn't being nasty */ + if (raw_trans_oob(req, param_ofs, param_count) || + raw_trans_oob(req, data_ofs, data_count)) { + DEBUG(1,("smb_raw_receive_trans: out of bounds parameters!\n")); + req->status = NT_STATUS_BUFFER_TOO_SMALL; + return cli_request_destroy(req); + } + + if (data_count) { + memcpy(parms->out.data.data + data_disp, + req->in.hdr + data_ofs, + data_count); + } + + if (param_count) { + memcpy(parms->out.params.data + param_disp, + req->in.hdr + param_ofs, + param_count); + } + + parms->out.data.length += data_count; + parms->out.params.length += param_count; + + if (total_data <= parms->out.data.length && total_param <= parms->out.params.length) + break; + + /* to receive more requests we need to mark this request as not received */ + req->in.buffer = NULL; + + if (!cli_request_receive(req)) { + req->status = NT_STATUS_UNSUCCESSFUL; + return cli_request_destroy(req); + } + } + +failed: + return cli_request_destroy(req); +} + + +/**************************************************************************** + trans2 raw async interface - only BLOBs used in this interface. +note that this doesn't yet support multi-part requests +****************************************************************************/ +struct cli_request *smb_raw_trans2_send(struct cli_tree *tree, + struct smb_trans2 *parms) +{ + uint8 command = SMBtrans2; + int wct = 14 + parms->in.setup_count; + struct cli_request *req; + char *outdata,*outparam; + int data_sent, param_sent; + int i; + const int padding = 3; + + req = cli_request_setup(tree, command, wct, padding); + if (!req) { + return NULL; + } + + /* fill in SMB parameters */ + data_sent = parms->in.data.length; + param_sent = parms->in.params.length; + outparam = req->out.data + padding; + outdata = outparam + param_sent; + + /* make sure we don't leak data via the padding */ + memset(req->out.data, 0, padding); + + /* primary request */ + SSVAL(req->out.vwv,VWV(0),parms->in.params.length); + SSVAL(req->out.vwv,VWV(1),parms->in.data.length); + SSVAL(req->out.vwv,VWV(2),parms->in.max_param); + SSVAL(req->out.vwv,VWV(3),parms->in.max_data); + SSVAL(req->out.vwv,VWV(4),parms->in.max_setup); + SSVAL(req->out.vwv,VWV(5),parms->in.flags); + SIVAL(req->out.vwv,VWV(6),parms->in.timeout); + SSVAL(req->out.vwv,VWV(8),0); /* reserved */ + SSVAL(req->out.vwv,VWV(9),parms->in.params.length); + SSVAL(req->out.vwv,VWV(10),PTR_DIFF(outparam,req->out.hdr)); + SSVAL(req->out.vwv,VWV(11),parms->in.data.length); + SSVAL(req->out.vwv,VWV(12),PTR_DIFF(outdata,req->out.hdr)); + SSVAL(req->out.vwv,VWV(13),parms->in.setup_count); + for (i=0;iin.setup_count;i++) { + SSVAL(req->out.vwv,VWV(14)+i*2,parms->in.setup[i]); + } + if (parms->in.params.data) { + cli_req_append_blob(req, &parms->in.params); + } + if (parms->in.data.data) { + cli_req_append_blob(req, &parms->in.data); + } + + if (!cli_request_send(req)) { + cli_request_destroy(req); + return NULL; + } + + return req; +} + +/* + trans2 synchronous blob interface +*/ +NTSTATUS smb_raw_trans2(struct cli_tree *tree, + TALLOC_CTX *mem_ctx, + struct smb_trans2 *parms) +{ + struct cli_request *req; + req = smb_raw_trans2_send(tree, parms); + if (!req) return NT_STATUS_UNSUCCESSFUL; + return smb_raw_trans2_recv(req, mem_ctx, parms); +} + + +/**************************************************************************** + receive a SMB nttrans response allocating the necessary memory + ****************************************************************************/ +NTSTATUS smb_raw_nttrans_recv(struct cli_request *req, + TALLOC_CTX *mem_ctx, + struct smb_nttrans *parms) +{ + uint32 total_data, recvd_data=0; + uint32 total_param, recvd_param=0; + + if (!cli_request_receive(req) || + cli_request_is_error(req)) { + return cli_request_destroy(req); + } + + /* sanity check */ + if (CVAL(req->in.hdr, HDR_COM) != SMBnttrans) { + DEBUG(0,("smb_raw_receive_nttrans: Expected %s response, got command 0x%02x\n", + "SMBnttrans", + CVAL(req->in.hdr,HDR_COM))); + req->status = NT_STATUS_UNSUCCESSFUL; + return cli_request_destroy(req); + } + + CLI_CHECK_MIN_WCT(req, 18); + + /* parse out the lengths */ + total_param = IVAL(req->in.vwv, 3); + total_data = IVAL(req->in.vwv, 7); + + parms->out.data = data_blob_talloc(mem_ctx, NULL, total_data); + parms->out.params = data_blob_talloc(mem_ctx, NULL, total_param); + + if (parms->out.data.length != total_data || + parms->out.params.length != total_param) { + req->status = NT_STATUS_NO_MEMORY; + return cli_request_destroy(req); + } + + parms->out.setup_count = CVAL(req->in.vwv, 35); + CLI_CHECK_WCT(req, 18 + parms->out.setup_count); + + if (parms->out.setup_count > 0) { + int i; + parms->out.setup = talloc(mem_ctx, 2 * parms->out.setup_count); + if (!parms->out.setup) { + req->status = NT_STATUS_NO_MEMORY; + return cli_request_destroy(req); + } + for (i=0;iout.setup_count;i++) { + parms->out.setup[i] = SVAL(req->in.vwv, VWV(18+i)); + } + } + + while (recvd_data < total_data || + recvd_param < total_param) { + uint32 param_count, param_ofs, param_disp; + uint32 data_count, data_ofs, data_disp; + uint32 total_data2, total_param2; + + /* parse out the total lengths again - they can shrink! */ + total_param2 = IVAL(req->in.vwv, 3); + total_data2 = IVAL(req->in.vwv, 7); + + if (total_data2 > total_data || + total_param2 > total_param) { + /* they must *only* shrink */ + DEBUG(1,("smb_raw_receive_nttrans: data/params expanded!\n")); + req->status = NT_STATUS_BUFFER_TOO_SMALL; + return cli_request_destroy(req); + } + + total_data = total_data2; + total_param = total_param2; + parms->out.data.length = total_data; + parms->out.params.length = total_param; + + /* parse params for this lump */ + param_count = IVAL(req->in.vwv, 11); + param_ofs = IVAL(req->in.vwv, 15); + param_disp = IVAL(req->in.vwv, 19); + + data_count = IVAL(req->in.vwv, 23); + data_ofs = IVAL(req->in.vwv, 27); + data_disp = IVAL(req->in.vwv, 31); + + if (data_count + data_disp > total_data || + param_count + param_disp > total_param) { + DEBUG(1,("smb_raw_receive_nttrans: Buffer overflow\n")); + req->status = NT_STATUS_BUFFER_TOO_SMALL; + return cli_request_destroy(req); + } + + /* check the server isn't being nasty */ + if (raw_trans_oob(req, param_ofs, param_count) || + raw_trans_oob(req, data_ofs, data_count)) { + DEBUG(1,("smb_raw_receive_nttrans: out of bounds parameters!\n")); + req->status = NT_STATUS_BUFFER_TOO_SMALL; + return cli_request_destroy(req); + } + + if (data_count) { + memcpy(parms->out.data.data + data_disp, + req->in.hdr + data_ofs, + data_count); + } + + if (param_count) { + memcpy(parms->out.params.data + param_disp, + req->in.hdr + param_ofs, + param_count); + } + + recvd_param += param_count; + recvd_data += data_count; + + if (recvd_data >= total_data && + recvd_param >= total_param) { + break; + } + + if (!cli_request_receive(req) || + cli_request_is_error(req)) { + return cli_request_destroy(req); + } + + /* sanity check */ + if (CVAL(req->in.hdr, HDR_COM) != SMBnttrans) { + DEBUG(0,("smb_raw_receive_nttrans: Expected nttranss, got command 0x%02x\n", + CVAL(req->in.hdr, HDR_COM))); + req->status = NT_STATUS_UNSUCCESSFUL; + return cli_request_destroy(req); + } + } + +failed: + return cli_request_destroy(req); +} + + +/**************************************************************************** + nttrans raw - only BLOBs used in this interface. + at the moment we only handle a single primary request +****************************************************************************/ +struct cli_request *smb_raw_nttrans_send(struct cli_tree *tree, + struct smb_nttrans *parms) +{ + struct cli_request *req; + char *outdata, *outparam; + int i; + int align = 0; + + /* only align if there are parameters or data */ + if (parms->in.params.length || parms->in.data.length) { + align = 3; + } + + req = cli_request_setup(tree, SMBnttrans, + 19 + parms->in.setup_count, + align + + parms->in.params.length + + parms->in.data.length); + if (!req) { + return NULL; + } + + /* fill in SMB parameters */ + outparam = req->out.data + align; + outdata = outparam + parms->in.params.length; + + SCVAL(req->out.vwv, 0, parms->in.max_setup); + SSVAL(req->out.vwv, 1, 0); /* reserved */ + SIVAL(req->out.vwv, 3, parms->in.params.length); + SIVAL(req->out.vwv, 7, parms->in.data.length); + SIVAL(req->out.vwv, 11, parms->in.max_param); + SIVAL(req->out.vwv, 15, parms->in.max_data); + SIVAL(req->out.vwv, 19, parms->in.params.length); + SIVAL(req->out.vwv, 23, PTR_DIFF(outparam,req->out.hdr)); + SIVAL(req->out.vwv, 27, parms->in.data.length); + SIVAL(req->out.vwv, 31, PTR_DIFF(outdata,req->out.hdr)); + SCVAL(req->out.vwv, 35, parms->in.setup_count); + SSVAL(req->out.vwv, 36, parms->in.function); + for (i=0;iin.setup_count;i++) { + SSVAL(req->out.vwv,VWV(19+i),parms->in.setup[i]); + } + if (parms->in.params.length) { + memcpy(outparam, parms->in.params.data, parms->in.params.length); + } + if (parms->in.data.length) { + memcpy(outparam, parms->in.data.data, parms->in.data.length); + } + + if (!cli_request_send(req)) { + cli_request_destroy(req); + return NULL; + } + + return req; +} + + +/**************************************************************************** + receive a SMB nttrans response allocating the necessary memory + ****************************************************************************/ +NTSTATUS smb_raw_nttrans(struct cli_tree *tree, + TALLOC_CTX *mem_ctx, + struct smb_nttrans *parms) +{ + struct cli_request *req; + + req = smb_raw_nttrans_send(tree, parms); + if (!req) { + return NT_STATUS_UNSUCCESSFUL; + } + + return smb_raw_nttrans_recv(req, mem_ctx, parms); +} diff --git a/source4/libcli/raw/smb_signing.c b/source4/libcli/raw/smb_signing.c new file mode 100644 index 0000000000..2ab61aa001 --- /dev/null +++ b/source4/libcli/raw/smb_signing.c @@ -0,0 +1,341 @@ +/* + Unix SMB/CIFS implementation. + SMB Signing Code + Copyright (C) Jeremy Allison 2002. + Copyright (C) Andrew Bartlett 2002-2003 + Copyright (C) James J Myers 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 smb_basic_signing_context { + DATA_BLOB mac_key; + uint32 next_seq_num; +}; + +/*********************************************************** + SMB signing - Common code before we set a new signing implementation +************************************************************/ +static BOOL set_smb_signing_common(struct cli_transport *transport) +{ + if (!(transport->negotiate.sec_mode & + (NEGOTIATE_SECURITY_SIGNATURES_REQUIRED|NEGOTIATE_SECURITY_SIGNATURES_ENABLED))) { + return False; + } + + if (transport->negotiate.sign_info.doing_signing) { + return False; + } + + if (transport->negotiate.sign_info.free_signing_context) + transport->negotiate.sign_info.free_signing_context(transport); + + /* These calls are INCOMPATIBLE with SMB signing */ + transport->negotiate.readbraw_supported = False; + transport->negotiate.writebraw_supported = False; + + return True; +} + +/*********************************************************** + SMB signing - Common code for 'real' implementations +************************************************************/ +static BOOL set_smb_signing_real_common(struct cli_transport *transport) +{ + if (transport->negotiate.sec_mode & NEGOTIATE_SECURITY_SIGNATURES_REQUIRED) { + DEBUG(5, ("Mandatory SMB signing enabled!\n")); + transport->negotiate.sign_info.doing_signing = True; + } + + DEBUG(5, ("SMB signing enabled!\n")); + + return True; +} + +static void mark_packet_signed(struct cli_request *req) +{ + uint16 flags2; + flags2 = SVAL(req->out.hdr, HDR_FLG2); + flags2 |= FLAGS2_SMB_SECURITY_SIGNATURES; + SSVAL(req->out.hdr, HDR_FLG2, flags2); +} + +static BOOL signing_good(struct cli_request *req, BOOL good) +{ + if (good && !req->transport->negotiate.sign_info.doing_signing) { + req->transport->negotiate.sign_info.doing_signing = True; + } + + if (!good) { + if (req->transport->negotiate.sign_info.doing_signing) { + DEBUG(1, ("SMB signature check failed!\n")); + return False; + } else { + DEBUG(3, ("Server did not sign reply correctly\n")); + cli_transport_free_signing_context(req->transport); + return False; + } + } + return True; +} + +/*********************************************************** + SMB signing - Simple implementation - calculate a MAC to send. +************************************************************/ +static void cli_request_simple_sign_outgoing_message(struct cli_request *req) +{ + unsigned char calc_md5_mac[16]; + struct MD5Context md5_ctx; + struct smb_basic_signing_context *data = req->transport->negotiate.sign_info.signing_context; + +#if 0 + /* enable this when packet signing is preventing you working out why valgrind + says that data is uninitialised */ + file_save("pkt.dat", req->out.buffer, req->out.size); +#endif + + req->seq_num = data->next_seq_num; + + /* some requests (eg. NTcancel) are one way, and the sequence number + should be increased by 1 not 2 */ + if (req->one_way_request) { + data->next_seq_num += 1; + } else { + data->next_seq_num += 2; + } + + /* + * Firstly put the sequence number into the first 4 bytes. + * and zero out the next 4 bytes. + */ + SIVAL(req->out.hdr, HDR_SS_FIELD, req->seq_num); + SIVAL(req->out.hdr, HDR_SS_FIELD + 4, 0); + + /* mark the packet as signed - BEFORE we sign it...*/ + mark_packet_signed(req); + + /* Calculate the 16 byte MAC and place first 8 bytes into the field. */ + MD5Init(&md5_ctx); + MD5Update(&md5_ctx, data->mac_key.data, + data->mac_key.length); + MD5Update(&md5_ctx, + req->out.buffer + NBT_HDR_SIZE, + req->out.size - NBT_HDR_SIZE); + MD5Final(calc_md5_mac, &md5_ctx); + + memcpy(&req->out.hdr[HDR_SS_FIELD], calc_md5_mac, 8); + +/* req->out.hdr[HDR_SS_FIELD+2]=0; + Uncomment this to test if the remote server actually verifies signitures...*/ +} + + +/*********************************************************** + SMB signing - Simple implementation - check a MAC sent by server. +************************************************************/ +static BOOL cli_request_simple_check_incoming_message(struct cli_request *req) +{ + BOOL good; + unsigned char calc_md5_mac[16]; + unsigned char server_sent_mac[8]; + unsigned char sequence_buf[8]; + struct MD5Context md5_ctx; + struct smb_basic_signing_context *data = req->transport->negotiate.sign_info.signing_context; + const size_t offset_end_of_sig = (HDR_SS_FIELD + 8); + int i; + const int sign_range = 0; + + /* its quite bogus to be guessing sequence numbers, but very useful + when debugging signing implementations */ + for (i = 1-sign_range; i <= 1+sign_range; i++) { + /* + * Firstly put the sequence number into the first 4 bytes. + * and zero out the next 4 bytes. + */ + SIVAL(sequence_buf, 0, req->seq_num+i); + SIVAL(sequence_buf, 4, 0); + + /* get a copy of the server-sent mac */ + memcpy(server_sent_mac, &req->in.hdr[HDR_SS_FIELD], sizeof(server_sent_mac)); + + /* Calculate the 16 byte MAC and place first 8 bytes into the field. */ + MD5Init(&md5_ctx); + MD5Update(&md5_ctx, data->mac_key.data, + data->mac_key.length); + MD5Update(&md5_ctx, req->in.hdr, HDR_SS_FIELD); + MD5Update(&md5_ctx, sequence_buf, sizeof(sequence_buf)); + + MD5Update(&md5_ctx, req->in.hdr + offset_end_of_sig, + req->in.size - NBT_HDR_SIZE - (offset_end_of_sig)); + MD5Final(calc_md5_mac, &md5_ctx); + + good = (memcmp(server_sent_mac, calc_md5_mac, 8) == 0); + if (good) break; + } + + if (good && i != 1) { + DEBUG(0,("SIGNING OFFSET %d\n", i)); + } + + if (!good) { + DEBUG(5, ("cli_request_simple_check_incoming_message: BAD SIG: wanted SMB signature of\n")); + dump_data(5, calc_md5_mac, 8); + + DEBUG(5, ("cli_request_simple_check_incoming_message: BAD SIG: got SMB signature of\n")); + dump_data(5, server_sent_mac, 8); + } + return signing_good(req, good); +} + + +/*********************************************************** + SMB signing - Simple implementation - free signing context +************************************************************/ +static void cli_transport_simple_free_signing_context(struct cli_transport *transport) +{ + struct smb_basic_signing_context *data = transport->negotiate.sign_info.signing_context; + + data_blob_free(&data->mac_key); + SAFE_FREE(transport->negotiate.sign_info.signing_context); + + return; +} + + +/*********************************************************** + SMB signing - Simple implementation - setup the MAC key. +************************************************************/ +BOOL cli_transport_simple_set_signing(struct cli_transport *transport, + const uchar user_transport_key[16], const DATA_BLOB response) +{ + struct smb_basic_signing_context *data; + + if (!set_smb_signing_common(transport)) { + return False; + } + + if (!set_smb_signing_real_common(transport)) { + return False; + } + + data = smb_xmalloc(sizeof(*data)); + transport->negotiate.sign_info.signing_context = data; + + data->mac_key = data_blob(NULL, MIN(response.length + 16, 40)); + + memcpy(&data->mac_key.data[0], user_transport_key, 16); + memcpy(&data->mac_key.data[16],response.data, MIN(response.length, 40 - 16)); + + /* Initialise the sequence number */ + data->next_seq_num = 0; + + transport->negotiate.sign_info.sign_outgoing_message = cli_request_simple_sign_outgoing_message; + transport->negotiate.sign_info.check_incoming_message = cli_request_simple_check_incoming_message; + transport->negotiate.sign_info.free_signing_context = cli_transport_simple_free_signing_context; + + return True; +} + + +/*********************************************************** + SMB signing - NULL implementation - calculate a MAC to send. +************************************************************/ +static void cli_request_null_sign_outgoing_message(struct cli_request *req) +{ + /* we can't zero out the sig, as we might be trying to send a + transport request - which is NBT-level, not SMB level and doesn't + have the field */ +} + + +/*********************************************************** + SMB signing - NULL implementation - check a MAC sent by server. +************************************************************/ +static BOOL cli_request_null_check_incoming_message(struct cli_request *req) +{ + return True; +} + + +/*********************************************************** + SMB signing - NULL implementation - free signing context +************************************************************/ +static void cli_null_free_signing_context(struct cli_transport *transport) +{ +} + +/** + SMB signing - NULL implementation - setup the MAC key. + + @note Used as an initialisation only - it will not correctly + shut down a real signing mechanism +*/ +BOOL cli_null_set_signing(struct cli_transport *transport) +{ + transport->negotiate.sign_info.signing_context = NULL; + + transport->negotiate.sign_info.sign_outgoing_message = cli_request_null_sign_outgoing_message; + transport->negotiate.sign_info.check_incoming_message = cli_request_null_check_incoming_message; + transport->negotiate.sign_info.free_signing_context = cli_null_free_signing_context; + + return True; +} + + +/** + * Free the signing context + */ +void cli_transport_free_signing_context(struct cli_transport *transport) +{ + if (transport->negotiate.sign_info.free_signing_context) { + transport->negotiate.sign_info.free_signing_context(transport); + } + + cli_null_set_signing(transport); +} + + +/** + * Sign a packet with the current mechanism + */ +void cli_request_calculate_sign_mac(struct cli_request *req) +{ + req->transport->negotiate.sign_info.sign_outgoing_message(req); +} + + +/** + * Check a packet with the current mechanism + * @return False if we had an established signing connection + * which had a back checksum, True otherwise + */ +BOOL cli_request_check_sign_mac(struct cli_request *req) +{ + BOOL good; + + if (req->in.size < (HDR_SS_FIELD + 8)) { + good = False; + } else { + good = req->transport->negotiate.sign_info.check_incoming_message(req); + } + + if (!good && req->transport->negotiate.sign_info.doing_signing) { + return False; + } + + return True; +} diff --git a/source4/libcli/unexpected.c b/source4/libcli/unexpected.c new file mode 100644 index 0000000000..c80dfa0465 --- /dev/null +++ b/source4/libcli/unexpected.c @@ -0,0 +1,172 @@ +/* + 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; + TALLOC_CTX *mem_ctx; + + if (!tdbd) { + mem_ctx = talloc_init("receive_unexpected"); + if (!mem_ctx) return; + tdbd = tdb_open_log(lock_path(mem_ctx, "unexpected.tdb"), 0, + TDB_CLEAR_IF_FIRST|TDB_DEFAULT, + O_RDWR | O_CREAT, 0644); + talloc_destroy(mem_ctx); + 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; + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_init("receive_unexpected"); + if (!mem_ctx) return NULL; + tdb2 = tdb_open_log(lock_path(mem_ctx, "unexpected.tdb"), 0, 0, O_RDONLY, 0); + talloc_destroy(mem_ctx); + 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/source4/libcli/util/asn1.c b/source4/libcli/util/asn1.c new file mode 100644 index 0000000000..09d4fbb6c9 --- /dev/null +++ b/source4/libcli/util/asn1.c @@ -0,0 +1,428 @@ +/* + 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 (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; + snprintf(el, sizeof(el), "%u", b/40); + pstrcat(oid, el); + snprintf(el, sizeof(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); + snprintf(el, sizeof(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); + *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); + *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/source4/libcli/util/clierror.c b/source4/libcli/util/clierror.c new file mode 100644 index 0000000000..4fa1daa3be --- /dev/null +++ b/source4/libcli/util/clierror.c @@ -0,0 +1,99 @@ +/* + Unix SMB/CIFS implementation. + client error handling routines + Copyright (C) Andrew Tridgell 1994-1998 + Copyright (C) James Myers 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 an error message from the last response +****************************************************************************/ +const char *cli_errstr(struct cli_state *cli) +{ + switch (cli->transport->error.etype) { + case ETYPE_DOS: + return dos_errstr(cli->transport->error.e.dos.eclass, + cli->transport->error.e.dos.ecode); + case ETYPE_NT: + return nt_errstr(cli->transport->error.e.nt_status); + + case ETYPE_SOCKET: + return "socket_error"; + + case ETYPE_NBT: + return "nbt_error"; + + case ETYPE_NONE: + return "no_error"; + } + return NULL; +} + + +/* Return the 32-bit NT status code from the last packet */ +NTSTATUS cli_nt_error(struct cli_state *cli) +{ + switch (cli->transport->error.etype) { + case ETYPE_NT: + return cli->transport->error.e.nt_status; + + case ETYPE_DOS: + return dos_to_ntstatus(cli->transport->error.e.dos.eclass, + cli->transport->error.e.dos.ecode); + case ETYPE_SOCKET: + return NT_STATUS_UNSUCCESSFUL; + + case ETYPE_NBT: + return NT_STATUS_UNSUCCESSFUL; + + case ETYPE_NONE: + return NT_STATUS_OK; + } + + return NT_STATUS_UNSUCCESSFUL; +} + + +/* Return 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) +{ + if (cli->transport->error.etype == ETYPE_DOS) { + ntstatus_to_dos(cli->transport->error.e.nt_status, + eclass, ecode); + return; + } + + if (eclass) *eclass = cli->transport->error.e.dos.eclass; + if (ecode) *ecode = cli->transport->error.e.dos.ecode; +} + + +/* Return true if the last packet was an error */ +BOOL cli_is_error(struct cli_state *cli) +{ + return NT_STATUS_IS_ERR(cli_nt_error(cli)); +} + +/* Return true if the last error was a DOS error */ +BOOL cli_is_dos_error(struct cli_state *cli) +{ + return cli->transport->error.etype == ETYPE_DOS; +} diff --git a/source4/libcli/util/cliutil.c b/source4/libcli/util/cliutil.c new file mode 100644 index 0000000000..47f94992a4 --- /dev/null +++ b/source4/libcli/util/cliutil.c @@ -0,0 +1,110 @@ +/* + Unix SMB/CIFS implementation. + client utility routines + Copyright (C) Andrew Tridgell 2001 + Copyright (C) James Myers 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 nicked from lib/util.c needed by client. +*******************************************************************/ + +/* a default finfo structure to ensure all fields are sensible */ +file_info def_finfo = {-1,0,0,0,0,0,0,"",""}; + +/******************************************************************* + A wrapper that handles case sensitivity and the special handling + of the ".." name. +*******************************************************************/ + +BOOL mask_match(struct cli_state *cli, const char *string, char *pattern, BOOL is_case_sensitive) +{ + fstring p2, s2; + + if (strcmp(string,"..") == 0) + string = "."; + if (strcmp(pattern,".") == 0) + return False; + + if (is_case_sensitive) + return ms_fnmatch(pattern, string, + cli->transport->negotiate.protocol) == 0; + + fstrcpy(p2, pattern); + fstrcpy(s2, string); + strlower(p2); + strlower(s2); + return ms_fnmatch(p2, s2, cli->transport->negotiate.protocol) == 0; +} + +/**************************************************************************** + 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); +} + +/******************************************************************* + 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); +} diff --git a/source4/libcli/util/credentials.c b/source4/libcli/util/credentials.c new file mode 100644 index 0000000000..0d521bae8a --- /dev/null +++ b/source4/libcli/util/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/source4/libcli/util/doserr.c b/source4/libcli/util/doserr.c new file mode 100644 index 0000000000..28bad6109d --- /dev/null +++ b/source4/libcli/util/doserr.c @@ -0,0 +1,91 @@ +/* + * 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 }, + { NULL, W_ERROR(0) } +}; + +/***************************************************************************** + returns a windows error message. not amazingly helpful, but better than a number. + *****************************************************************************/ +const char *win_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/source4/libcli/util/errormap.c b/source4/libcli/util/errormap.c new file mode 100644 index 0000000000..a257c2d0ea --- /dev/null +++ b/source4/libcli/util/errormap.c @@ -0,0 +1,1546 @@ +/* + * 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}, + {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_OBJECT_PATH_NOT_FOUND }, + { 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 }, + { EISDIR, ERRDOS, ERRbadpath, 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 +#ifdef EFBIG + { EBUSY, ERRDOS, ERRbadshare, NT_STATUS_SHARING_VIOLATION }, +#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/source4/libcli/util/nterr.c b/source4/libcli/util/nterr.c new file mode 100644 index 0000000000..6c4b7c8417 --- /dev/null +++ b/source4/libcli/util/nterr.c @@ -0,0 +1,715 @@ +/* + * 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 struct +{ + const char *nt_errstr; + NTSTATUS nt_errcode; +} nt_err_code_struct; + +static const 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_NO_MORE_ENTRIES", NT_STATUS_NO_MORE_ENTRIES }, + { "STATUS_MORE_ENTRIES", STATUS_MORE_ENTRIES }, + { "STATUS_SOME_UNMAPPED", STATUS_SOME_UNMAPPED }, + { NULL, NT_STATUS(0) } +}; + +static const nt_err_code_struct nt_err_desc[] = +{ + { "Success", NT_STATUS_OK }, + { "Undetermined error", NT_STATUS_UNSUCCESSFUL }, + { "Access denied", NT_STATUS_ACCESS_DENIED }, + { "Account locked out", NT_STATUS_ACCOUNT_LOCKED_OUT }, + { "Must change password", NT_STATUS_PASSWORD_MUST_CHANGE }, + { "Password is too short", NT_STATUS_PWD_TOO_SHORT }, + { "Password is too recent", NT_STATUS_PWD_TOO_RECENT }, + { "Password history conflict", NT_STATUS_PWD_HISTORY_CONFLICT }, + { "No logon servers", NT_STATUS_NO_LOGON_SERVERS }, + { "Improperly formed account name", NT_STATUS_INVALID_ACCOUNT_NAME }, + { "User exists", NT_STATUS_USER_EXISTS }, + { "No such user", NT_STATUS_NO_SUCH_USER }, + { "Group exists", NT_STATUS_GROUP_EXISTS }, + { "No such group", NT_STATUS_NO_SUCH_GROUP }, + { "Member not in group", NT_STATUS_MEMBER_NOT_IN_GROUP }, + { "Wrong Password", NT_STATUS_WRONG_PASSWORD }, + { "Ill formed password", NT_STATUS_ILL_FORMED_PASSWORD }, + { "Password restriction", NT_STATUS_PASSWORD_RESTRICTION }, + { "Logon failure", NT_STATUS_LOGON_FAILURE }, + { "Account restriction", NT_STATUS_ACCOUNT_RESTRICTION }, + { "Invalid logon hours", NT_STATUS_INVALID_LOGON_HOURS }, + { "Invalid workstation", NT_STATUS_INVALID_WORKSTATION }, + { "Password expired", NT_STATUS_PASSWORD_EXPIRED }, + { "Account disabled", NT_STATUS_ACCOUNT_DISABLED }, + { "Unexpected information received", NT_STATUS_INVALID_PARAMETER }, + { "Memory allocation error", NT_STATUS_NO_MEMORY }, + { "No domain controllers located", NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND }, + { "Account locked out", NT_STATUS_ACCOUNT_LOCKED_OUT }, + { "Named pipe not available", NT_STATUS_PIPE_NOT_AVAILABLE }, + { "Not implemented", NT_STATUS_NOT_IMPLEMENTED }, + { "Invalid information class", NT_STATUS_INVALID_INFO_CLASS }, + { "Information length mismatch", NT_STATUS_INFO_LENGTH_MISMATCH }, + { "Access violation", NT_STATUS_ACCESS_VIOLATION }, + { "Invalid handle", NT_STATUS_INVALID_HANDLE }, + { "Invalid parameter", NT_STATUS_INVALID_PARAMETER }, + { "No memory", NT_STATUS_NO_MEMORY }, + { "Buffer too small", NT_STATUS_BUFFER_TOO_SMALL }, + { "Revision mismatch", NT_STATUS_REVISION_MISMATCH }, + { "No logon servers", NT_STATUS_NO_LOGON_SERVERS }, + { "No such logon session", NT_STATUS_NO_SUCH_LOGON_SESSION }, + { "No such privilege", NT_STATUS_NO_SUCH_PRIVILEGE }, + { "Procedure not found", NT_STATUS_PROCEDURE_NOT_FOUND }, + { "Server disabled", NT_STATUS_SERVER_DISABLED }, + { "Invalid pipe state", NT_STATUS_INVALID_PIPE_STATE }, + { "Named pipe busy", NT_STATUS_PIPE_BUSY }, + { "Illegal function", NT_STATUS_ILLEGAL_FUNCTION }, + { "Named pipe dicconnected", NT_STATUS_PIPE_DISCONNECTED }, + { "Named pipe closing", NT_STATUS_PIPE_CLOSING }, + { "Remote host not listening", NT_STATUS_REMOTE_NOT_LISTENING }, + { "Duplicate name on network", NT_STATUS_DUPLICATE_NAME }, + { "Print queue is full", NT_STATUS_PRINT_QUEUE_FULL }, + { "No print spool space available", NT_STATUS_NO_SPOOL_SPACE }, + { "Too many names", NT_STATUS_TOO_MANY_NAMES }, + { "Too many sessions", NT_STATUS_TOO_MANY_SESSIONS }, + { "Invalid server state", NT_STATUS_INVALID_SERVER_STATE }, + { "Invalid domain state", NT_STATUS_INVALID_DOMAIN_STATE }, + { "Invalid domain role", NT_STATUS_INVALID_DOMAIN_ROLE }, + { "No such domain", NT_STATUS_NO_SUCH_DOMAIN }, + { "Domain exists", NT_STATUS_DOMAIN_EXISTS }, + { "Domain limit exceeded", NT_STATUS_DOMAIN_LIMIT_EXCEEDED }, + { "Bad logon session state", NT_STATUS_BAD_LOGON_SESSION_STATE }, + { "Logon session collision", NT_STATUS_LOGON_SESSION_COLLISION }, + { "Invalid logon type", NT_STATUS_INVALID_LOGON_TYPE }, + { "Cancelled", NT_STATUS_CANCELLED }, + { "Invalid computer name", NT_STATUS_INVALID_COMPUTER_NAME }, + { "Logon server conflict", NT_STATUS_LOGON_SERVER_CONFLICT }, + { "Time difference at domain controller", NT_STATUS_TIME_DIFFERENCE_AT_DC }, + { "Pipe broken", NT_STATUS_PIPE_BROKEN }, + { "Registry corrupt", NT_STATUS_REGISTRY_CORRUPT }, + { "Too many secrets", NT_STATUS_TOO_MANY_SECRETS }, + { "Too many SIDs", NT_STATUS_TOO_MANY_SIDS }, + { "Lanmanager cross encryption required", NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED }, + { "Log file full", NT_STATUS_LOG_FILE_FULL }, + { "No trusted LSA secret", NT_STATUS_NO_TRUST_LSA_SECRET }, + { "No trusted SAM account", NT_STATUS_NO_TRUST_SAM_ACCOUNT }, + { "Trusted domain failure", NT_STATUS_TRUSTED_DOMAIN_FAILURE }, + { "Trust relationship failure", NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE }, + { "Trust failure", NT_STATUS_TRUST_FAILURE }, + { "Netlogon service not started", NT_STATUS_NETLOGON_NOT_STARTED }, + { "Account expired", NT_STATUS_ACCOUNT_EXPIRED }, + { "Network credential conflict", NT_STATUS_NETWORK_CREDENTIAL_CONFLICT }, + { "Remote session limit", NT_STATUS_REMOTE_SESSION_LIMIT }, + { "No logon interdomain trust account", NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT }, + { "No logon workstation trust account", NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT }, + { "No logon server trust account", NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT }, + { "Domain trust inconsistent", NT_STATUS_DOMAIN_TRUST_INCONSISTENT }, + { "No user session key available", NT_STATUS_NO_USER_SESSION_KEY }, + { "User session deleted", NT_STATUS_USER_SESSION_DELETED }, + { "Insufficient server resources", NT_STATUS_INSUFF_SERVER_RESOURCES }, + { "Insufficient logon information", NT_STATUS_INSUFFICIENT_LOGON_INFO }, + + { "License quota exceeded", NT_STATUS_LICENSE_QUOTA_EXCEEDED }, + + { NULL, NT_STATUS(0) } +}; + + +/***************************************************************************** + returns an NT error message. not amazingly helpful, but better than a number. + *****************************************************************************/ +const char *nt_errstr(NTSTATUS nt_code) +{ + static 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/source4/libcli/util/ntlmssp_sign.c b/source4/libcli/util/ntlmssp_sign.c new file mode 100644 index 0000000000..bd6d64d842 --- /dev/null +++ b/source4/libcli/util/ntlmssp_sign.c @@ -0,0 +1,226 @@ +/* + * 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, 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[16], char digest[16], + const char encrypted_response[16], + const char *constant) +{ + struct MD5Context ctx3; + + MD5Init(&ctx3); + MD5Update(&ctx3, encrypted_response, 5); + MD5Update(&ctx3, constant, strlen(constant)); + MD5Final(digest, &ctx3); + + calc_hash(hash, digest, 16); +} + +enum ntlmssp_direction { + NTLMSSP_SEND, + NTLMSSP_RECEIVE +}; + +static NTSTATUS ntlmssp_make_packet_signiture(NTLMSSP_CLIENT_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; + char seq_num[4]; + uchar digest[16]; + SIVAL(seq_num, 0, ntlmssp_state->ntlmssp_seq_num); + + hmac_md5_init_limK_to_64(ntlmssp_state->cli_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, "Bd", digest, sizeof(digest), ntlmssp_state->ntlmssp_seq_num)) { + return NT_STATUS_NO_MEMORY; + } + switch (direction) { + case NTLMSSP_SEND: + NTLMSSPcalc_ap(ntlmssp_state->cli_sign_hash, sig->data, sig->length); + break; + case NTLMSSP_RECEIVE: + NTLMSSPcalc_ap(ntlmssp_state->srv_sign_hash, sig->data, sig->length); + break; + } + } else { + uint32 crc; + crc = crc32_buffer(data, length); + if (!msrpc_gen(sig, "ddd", 0, crc, ntlmssp_state->ntlmssp_seq_num)) { + return NT_STATUS_NO_MEMORY; + } + + NTLMSSPcalc_ap(ntlmssp_state->ntlmssp_hash, sig->data, sig->length); + } + return NT_STATUS_OK; +} + +NTSTATUS ntlmssp_client_sign_packet(NTLMSSP_CLIENT_STATE *ntlmssp_state, + const uchar *data, size_t length, + DATA_BLOB *sig) +{ + ntlmssp_state->ntlmssp_seq_num++; + return ntlmssp_make_packet_signiture(ntlmssp_state, data, length, NTLMSSP_SEND, sig); +} + +/** + * Check the signature of an incoming packet + * @note caller *must* check that the signature is the size it expects + * + */ + +NTSTATUS ntlmssp_client_check_packet(NTLMSSP_CLIENT_STATE *ntlmssp_state, + const uchar *data, size_t length, + const DATA_BLOB *sig) +{ + DATA_BLOB local_sig; + NTSTATUS nt_status; + + if (sig->length < 8) { + DEBUG(0, ("NTLMSSP packet check failed due to short signiture (%u bytes)!\n", + sig->length)); + } + + nt_status = ntlmssp_make_packet_signiture(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, local_sig.data, MIN(sig->length, local_sig.length)) == 0) { + return NT_STATUS_OK; + } else { + DEBUG(5, ("BAD SIG: wanted signature of\n")); + dump_data(5, local_sig.data, local_sig.length); + + DEBUG(5, ("BAD SIG: got signature of\n")); + dump_data(5, sig->data, sig->length); + + DEBUG(0, ("NTLMSSP packet check failed due to invalid signiture!\n")); + return NT_STATUS_ACCESS_DENIED; + } +} + +/** + Initialise the state for NTLMSSP signing. +*/ +NTSTATUS ntlmssp_client_sign_init(NTLMSSP_CLIENT_STATE *ntlmssp_state) +{ + unsigned char p24[24]; + unsigned char lm_hash[16]; + + if (!ntlmssp_state->lm_resp.data) { + /* can't sign or check signitures yet */ + return NT_STATUS_UNSUCCESSFUL; + } + + E_deshash(ntlmssp_state->password, lm_hash); + + NTLMSSPOWFencrypt(lm_hash, ntlmssp_state->lm_resp.data, p24); + + if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) + { + calc_ntlmv2_hash(ntlmssp_state->cli_sign_hash, ntlmssp_state->cli_sign_const, p24, CLI_SIGN); + calc_ntlmv2_hash(ntlmssp_state->cli_seal_hash, ntlmssp_state->cli_seal_const, p24, CLI_SEAL); + calc_ntlmv2_hash(ntlmssp_state->srv_sign_hash, ntlmssp_state->srv_sign_const, p24, SRV_SIGN); + calc_ntlmv2_hash(ntlmssp_state->srv_seal_hash, ntlmssp_state->srv_seal_const, p24, SRV_SEAL); + } + else + { + char k2[8]; + memcpy(k2, p24, 5); + k2[5] = 0xe5; + k2[6] = 0x38; + k2[7] = 0xb0; + + calc_hash(ntlmssp_state->ntlmssp_hash, k2, 8); + } + + ntlmssp_state->ntlmssp_seq_num = 0; + + ZERO_STRUCT(lm_hash); + return NT_STATUS_OK; +} diff --git a/source4/libcli/util/pwd_cache.c b/source4/libcli/util/pwd_cache.c new file mode 100644 index 0000000000..0d84f04ee3 --- /dev/null +++ b/source4/libcli/util/pwd_cache.c @@ -0,0 +1,72 @@ +/* + 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 )); + memset((char *)pwd->smb_lm_pwd, '\0', sizeof(pwd->smb_lm_pwd)); + memset((char *)pwd->smb_nt_pwd, '\0', sizeof(pwd->smb_nt_pwd)); + memset((char *)pwd->smb_lm_owf, '\0', sizeof(pwd->smb_lm_owf)); + memset((char *)pwd->smb_nt_owf, '\0', sizeof(pwd->smb_nt_owf)); + + pwd->null_pwd = True; /* safest option... */ + pwd->cleartext = False; + pwd->crypted = False; +} + +/**************************************************************************** + Makes lm and nt hashed passwords. +****************************************************************************/ + +static void pwd_make_lm_nt_16(struct pwd_info *pwd, const char *clr) +{ + pstring dos_passwd; + + pwd_init(pwd); + + push_ascii_pstring(dos_passwd, clr); + + nt_lm_owf_gen(dos_passwd, pwd->smb_nt_pwd, pwd->smb_lm_pwd); + pwd->null_pwd = False; + pwd->cleartext = False; + pwd->crypted = False; +} + +/**************************************************************************** + Stores a cleartext password. +****************************************************************************/ + +void pwd_set_cleartext(struct pwd_info *pwd, const char *clr) +{ + pwd_init(pwd); + push_ascii_fstring(pwd->password, clr); + pwd->cleartext = True; + pwd->null_pwd = False; + pwd->crypted = False; + pwd_make_lm_nt_16(pwd, clr); +} + + diff --git a/source4/libcli/util/smbdes.c b/source4/libcli/util/smbdes.c new file mode 100644 index 0000000000..cde77f94a3 --- /dev/null +++ b/source4/libcli/util/smbdes.c @@ -0,0 +1,415 @@ +/* + 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>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]; + } +} + +/* 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/source4/libcli/util/smbencrypt.c b/source4/libcli/util/smbencrypt.c new file mode 100644 index 0000000000..00c2b58146 --- /dev/null +++ b/source4/libcli/util/smbencrypt.c @@ -0,0 +1,418 @@ +/* + 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 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 + */ + +void E_deshash(const char *passwd, uchar p16[16]) +{ + fstring dospwd; + ZERO_STRUCT(dospwd); + ZERO_STRUCTP(p16); + + /* Password must be converted to DOS charset - null terminated, uppercase. */ + push_ascii(dospwd, (const char *)passwd, sizeof(dospwd), STR_UPPER|STR_TERMINATE); + + /* Only the fisrt 14 chars are considered, password need not be null terminated. */ + E_P16(dospwd, p16); + + ZERO_STRUCT(dospwd); +} + +/** + * 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 +} + +BOOL make_oem_passwd_hash(char data[516], const char *passwd, uchar old_pw_hash[16], BOOL unicode) +{ + int new_pw_len = strlen(passwd) * (unicode ? 2 : 1); + + if (new_pw_len > 512) + { + DEBUG(0,("make_oem_passwd_hash: new password is too long.\n")); + return False; + } + + /* + * Now setup the data area. + * We need to generate a random fill + * for this area to make it harder to + * decrypt. JRA. + */ + generate_random_buffer((unsigned char *)data, 516, False); + push_string(NULL, &data[512 - new_pw_len], passwd, new_pw_len, + STR_NOALIGN | (unicode?STR_UNICODE:STR_ASCII)); + SIVAL(data, 512, new_pw_len); + +#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); + + return True; +} + +/* Does the md5 encryption from the NT hash 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]) +{ + 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]) +{ + mdfour((unsigned char *)sess_key, kr, 16); + +#ifdef DEBUG_PASSWORD + DEBUG(100, ("SMBsesskeygen_ntv1:\n")); + dump_data(100, sess_key, 16); +#endif +} + +DATA_BLOB NTLMv2_generate_response(uchar ntlm_v2_hash[16], + DATA_BLOB server_chal, size_t client_chal_length) +{ + uchar ntlmv2_response[16]; + DATA_BLOB ntlmv2_client_data; + DATA_BLOB final_response; + + /* NTLMv2 */ + + /* We also get to specify some random data */ + ntlmv2_client_data = data_blob(NULL, client_chal_length); + generate_random_buffer(ntlmv2_client_data.data, ntlmv2_client_data.length, False); + + /* Given that data, and the challenge from the server, generate a response */ + SMBOWFencrypt_ntv2(ntlm_v2_hash, server_chal, ntlmv2_client_data, ntlmv2_response); + + /* put it into nt_response, for the code below to put into the packet */ + final_response = data_blob(NULL, ntlmv2_client_data.length + sizeof(ntlmv2_response)); + memcpy(final_response.data, ntlmv2_response, sizeof(ntlmv2_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(ntlmv2_response), ntlmv2_client_data.data, ntlmv2_client_data.length); + data_blob_free(&ntlmv2_client_data); + + return final_response; +} + +BOOL SMBNTLMv2encrypt(const char *user, const char *domain, const char *password, + const DATA_BLOB server_chal, + DATA_BLOB *lm_response, DATA_BLOB *nt_response, + DATA_BLOB *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; + } + + *nt_response = NTLMv2_generate_response(ntlm_v2_hash, server_chal, 64 /* pick a number, > 8 */); + + /* LMv2 */ + + *lm_response = NTLMv2_generate_response(ntlm_v2_hash, server_chal, 8); + + *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, session_key->data); + + return True; +} + +/*********************************************************** + encode a password buffer. The caller gets to figure out + what to put in it. +************************************************************/ +BOOL encode_pw_buffer(char buffer[516], char *new_pw, int new_pw_length) +{ + generate_random_buffer((unsigned char *)buffer, 516, True); + + memcpy(&buffer[512 - new_pw_length], new_pw, new_pw_length); + + /* + * The length of the new password is in the last 4 bytes of + * the data buffer. + */ + SIVAL(buffer, 512, new_pw_length); + + 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 byte_len=0; + + /* + Warning !!! : This function is called from some rpc call. + The password IN the buffer is 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 128 characters */ + if ( (byte_len < 0) || (byte_len > new_pwrd_size - 1)) { + 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 must be a pstring */ + *new_pw_len = pull_string(NULL, new_pwrd, &in_buffer[512 - byte_len], new_pwrd_size, byte_len, STR_UNICODE); + +#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/source4/libcli/util/smberr.c b/source4/libcli/util/smberr.c new file mode 100644 index 0000000000..d6eabcfbce --- /dev/null +++ b/source4/libcli/util/smberr.c @@ -0,0 +1,181 @@ +/* + Unix SMB/CIFS implementation. + Copyright (C) Andrew Tridgell 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" + +/* + error code stuff - put together by Merik Karman + merik@blackadder.dsh.oz.au +*/ + +struct err_code_struct { + const char *name; + int code; + const char *message; +}; + +/* Dos Error Messages */ +static const struct 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 */ +static const struct 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 */ +static const struct 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}}; + + +static const struct { + uint8 class; + const char *class_name; + const struct 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 dos error string given a error class and error code */ +const char *dos_errstr(uint8 class, uint16 code) +{ + static char *msg; + int i, j; + const struct err_code_struct *err_msgs; + + if (msg) { + free(msg); + msg = NULL; + } + + for (i=0;err_classes[i].class_name;i++) { + if (class == err_classes[i].class) break; + } + if (!err_classes[i].class_name) { + asprintf(&msg, "Unknown DOS error %d:%d\n", class, code); + return msg; + } + + err_msgs = err_classes[i].err_msgs; + + for (j=0;err_msgs && err_msgs[j].name;j++) { + if (err_msgs[j].code == code) { + asprintf(&msg, "%s:%s (%s)\n", + err_classes[i].class_name, + err_msgs[j].name, + err_msgs[j].message); + return msg; + } + } + + asprintf(&msg, "Unknown DOS error %s:%d\n", err_classes[i].class_name, code); + return msg; +} diff --git a/source4/locking/brlock.c b/source4/locking/brlock.c new file mode 100644 index 0000000000..4cd885f1a6 --- /dev/null +++ b/source4/locking/brlock.c @@ -0,0 +1,698 @@ +/* + 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; icontext.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_log(lock_path("brlock.tdb"), 0, TDB_DEFAULT|(read_only?0x0:TDB_CLEAR_IF_FIRST), + read_only?O_RDONLY:(O_RDWR|O_CREAT), 0644); + 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) +{ + 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; + + 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; istart <= 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) +{ + 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; ilock_type == WRITE_LOCK && + brl_same_context(&lock->context, &context) && + lock->fnum == fnum && + lock->start == start && + lock->size == size) { + /* 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; icontext, &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) { + /* Send unlock messages to any pending waiters that overlap. */ + for (j=0; jlock_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; icontext.tid == tid && + lock->context.pid == pid && + lock->fnum == fnum) { + + /* Send unlock messages to any pending waiters that overlap. */ + for (j=0; jlock_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;idevice, 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/source4/locking/locking.c b/source4/locking/locking.c new file mode 100644 index 0000000000..63e1f3a164 --- /dev/null +++ b/source4/locking/locking.c @@ -0,0 +1,849 @@ +/* + 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" + +/* 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,struct tcon_context *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,struct tcon_context *conn, uint16 lock_pid, + SMB_BIG_UINT count,SMB_BIG_UINT offset,enum brl_type lock_type) +{ + 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); + + 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)) { + status = NT_STATUS_LOCK_NOT_GRANTED; + /* + * 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); + } + } + } + + 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,struct tcon_context *conn, uint16 lock_pid, + SMB_BIG_UINT count,SMB_BIG_UINT offset,enum brl_type lock_type) +{ + 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); + 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; + } + if (sleeptime) + sys_usleep(sleeptime); + } + return ret; +} + +/**************************************************************************** + Utility function called by unlocking requests. +****************************************************************************/ + +NTSTATUS do_unlock(files_struct *fsp,struct tcon_context *conn, uint16 lock_pid, + SMB_BIG_UINT count,SMB_BIG_UINT offset) +{ + BOOL ok = False; + + 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. + */ + + ok = brl_unlock(fsp->dev, fsp->inode, fsp->fnum, + lock_pid, sys_getpid(), conn->cnum, offset, count, False); + + if (!ok) { + DEBUG(10,("do_unlock: returning ERRlock.\n" )); + return NT_STATUS_RANGE_NOT_LOCKED; + } + + if (!lp_posix_locking(SNUM(conn))) + return NT_STATUS_OK; + + (void)release_posix_lock(fsp, offset, count); + + 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_log(lock_path("locking.tdb"), + 0, TDB_DEFAULT|(read_only?0x0:TDB_CLEAR_IF_FIRST), + read_only?O_RDONLY:O_RDWR|O_CREAT, + 0644); + + 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(struct tcon_context *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(struct tcon_context *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. +********************************************************************/ + +static 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 = %u, share_mode = 0x%x, desired_access = 0x%x, port = 0x%x, type= 0x%x, file_id = %lu, dev = 0x%x, inode = %.0f", + num, 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(struct tcon_context *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; + + *pp_shares = NULL; + + dbuf = tdb_fetch(tdb, locking_key(dev, inode)); + 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, locking_key(dev, inode), 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; + + if (ppse) + *ppse = NULL; + + /* read in the existing share modes */ + dbuf = tdb_fetch(tdb, locking_key(dev, inode)); + 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;iu.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, locking_key(dev, inode)) == -1) + count = -1; + } else { + if (tdb_store(tdb, locking_key(dev, inode), 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; + BOOL ret = True; + + /* read in the existing share modes if any */ + dbuf = tdb_fetch(tdb, locking_key_fsp(fsp)); + 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, locking_key_fsp(fsp), 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, locking_key_fsp(fsp), 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; + + /* read in the existing share modes */ + dbuf = tdb_fetch(tdb, locking_key(dev, inode)); + 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;iu.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, locking_key(dev, inode)) == -1) + ret = False; + } else { + if (tdb_store(tdb, locking_key(dev, inode), 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; + + /* read in the existing share modes */ + dbuf = tdb_fetch(tdb, locking_key(dev, inode)); + 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;iu.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, locking_key(dev,inode), 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;iu.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/source4/locking/posix.c b/source4/locking/posix.c new file mode 100644 index 0000000000..81690bace9 --- /dev/null +++ b/source4/locking/posix.c @@ -0,0 +1,1313 @@ +/* + 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 tcon_context *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 = conn->vfs_ops.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 (conn->vfs_ops.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 = conn->vfs_ops.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; ifd == 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; + struct tcon_context *conn = fsp->conn; + + DEBUG(8,("posix_fcntl_lock %d %d %.0f %.0f %d\n",fsp->fd,op,(double)offset,(double)count,type)); + + ret = conn->vfs_ops.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 = conn->vfs_ops.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; ifd, + (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; ifsp_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/source4/modules/developer.c b/source4/modules/developer.c new file mode 100644 index 0000000000..a697abcd22 --- /dev/null +++ b/source4/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 init_module(void) +{ + smb_register_charset(&weird_functions); + return 1; +} diff --git a/source4/modules/mysql.c b/source4/modules/mysql.c new file mode 100644 index 0000000000..1d5819295b --- /dev/null +++ b/source4/modules/mysql.c @@ -0,0 +1,1043 @@ + +/* + * MySQL password backend for samba + * 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" +#include + +#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_UID_DEFAULT "uid" +#define CONFIG_GID_DEFAULT "gid" +#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_UNKNOWN_3_DEFAULT "unknown_3" +#define CONFIG_LOGON_DIVS_DEFAULT "logon_divs" +#define CONFIG_HOURS_LEN_DEFAULT "hours_len" +#define CONFIG_UNKNOWN_5_DEFAULT "unknown_5" +#define CONFIG_UNKNOWN_6_DEFAULT "unknown_6" +#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; + +/* Used to construct insert and update queries */ + +typedef struct pdb_mysql_query { + char update; + TALLOC_CTX *mem_ctx; + char *part1; + char *part2; +} pdb_mysql_query; +#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; \ + } \ +} + +static void pdb_mysql_int_field(struct pdb_methods *m, + struct pdb_mysql_query *q, 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); + } +} + +static NTSTATUS pdb_mysql_string_field(struct pdb_methods *methods, + struct pdb_mysql_query *q, + char *name, const char *value) +{ + char *esc_value; + struct pdb_mysql_data *data; + char *tmp_value; + + SET_DATA(data, methods); + + if (!name || !value || !strcmp(value, "") || strchr(name, '\'')) + return NT_STATUS_INVALID_PARAMETER; /* This field shouldn't be set by module */ + + esc_value = malloc(strlen(value) * 2 + 1); + + tmp_value = smb_xstrdup(value); + mysql_real_escape_string(data->handle, esc_value, tmp_value, + strlen(tmp_value)); + SAFE_FREE(tmp_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; +} + +static char * config_value(pdb_mysql_data * data, char *name, char *default_value) +{ + if (lp_parm_string(NULL, data->location, name)) + return lp_parm_string(NULL, data->location, name); + + return default_value; +} + +static char * config_value_write(pdb_mysql_data * data, char *name, char *default_value) { + char *v = config_value(data, name, NULL); + char *swrite; + + if (!v) + return default_value; + + swrite = strchr(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(pdb_mysql_data * data, char *name, char *default_value) +{ + char *v = config_value(data, name, NULL); + char *swrite; + + if (!v) + return default_value; + + swrite = strchr(v, ':'); + + /* If no write is specified, there are no problems */ + if (!swrite) { + if (strlen(v) == 0) + return "NULL"; + return v; + } + + /* Otherwise, we have to cut the ':write_part' */ + *swrite = '\0'; + if (strlen(v) == 0) + return "NULL"; + + return v; +} + +/* Wrapper for atol that returns 0 if 'a' points to NULL */ +static long xatol(char *a) +{ + long ret = 0; + + if (a != NULL) + ret = atol(a); + + return ret; +} + +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]) + pdb_set_uid(u, xatol(row[18]), PDB_SET); + if (row[19]) + pdb_set_gid(u, xatol(row[19]), PDB_SET); + + string_to_sid(&sid, row[20]); + pdb_set_user_sid(u, &sid, PDB_SET); + string_to_sid(&sid, row[21]); + pdb_set_group_sid(u, &sid, PDB_SET); + + if (pdb_gethexpwd(row[22], temp), PDB_SET) + pdb_set_lanman_passwd(u, temp, PDB_SET); + if (pdb_gethexpwd(row[23], temp), PDB_SET) + pdb_set_nt_passwd(u, temp, PDB_SET); + + /* Only use plaintext password storage when lanman and nt are + * NOT used */ + if (!row[22] || !row[23]) + pdb_set_plaintext_passwd(u, row[24]); + + pdb_set_acct_ctrl(u, xatol(row[25]), PDB_SET); + pdb_set_unknown_3(u, xatol(row[26]), PDB_SET); + pdb_set_logon_divs(u, xatol(row[27]), PDB_SET); + pdb_set_hours_len(u, xatol(row[28]), PDB_SET); + pdb_set_unknown_5(u, xatol(row[29]), PDB_SET); + pdb_set_unknown_6(u, xatol(row[30]), 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; + } + + 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,%s,%s FROM %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, "uid column", CONFIG_UID_DEFAULT), + config_value_read(data, "gid column", CONFIG_GID_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, "unknown 3 column", + CONFIG_UNKNOWN_3_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, "unknown 5 column", + CONFIG_UNKNOWN_5_DEFAULT), + config_value_read(data, "unknown 6 column", + CONFIG_UNKNOWN_6_DEFAULT), + config_value(data, "table", CONFIG_TABLE_DEFAULT) + ); + DEBUG(5, ("Executing query %s\n", query)); + + 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(%lu 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, + const char *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; + } + + DEBUG(5, + ("mysqlsam_select_by_field: getting data where %s = %s(nonescaped)\n", + field, sname)); + + 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; + } + + 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,%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, "uid column", CONFIG_UID_DEFAULT), + config_value_read(data, "gid column", CONFIG_GID_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, "unknown 3 column", + CONFIG_UNKNOWN_3_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, "unknown 5 column", + CONFIG_UNKNOWN_5_DEFAULT), + config_value_read(data, "unknown 6 column", + CONFIG_UNKNOWN_6_DEFAULT), + config_value(data, "table", CONFIG_TABLE_DEFAULT), 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, + config_value_read(data, "username column", + CONFIG_USERNAME_DEFAULT), 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, + config_value_read(data, "user sid column", + CONFIG_USER_SID_DEFAULT), 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); + + 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); + + 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) +{ + pstring temp; + struct pdb_mysql_data *data; + pdb_mysql_query query; + fstring sid_str; + + 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.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("mysqlsam_replace_sam_account"); + query.part2 = talloc_asprintf(query.mem_ctx, "%s", ""); + if (query.update) { + query.part1 = + talloc_asprintf(query.mem_ctx, "UPDATE %s SET ", + config_value(data, "table", + CONFIG_TABLE_DEFAULT)); + } else { + query.part1 = + talloc_asprintf(query.mem_ctx, "INSERT INTO %s (", + config_value(data, "table", + CONFIG_TABLE_DEFAULT)); + } + + pdb_mysql_int_field(methods, &query, + config_value_write(data, "acct ctrl column", + CONFIG_ACCT_CTRL_DEFAULT), + pdb_get_acct_ctrl(newpwd)); + + if (pdb_get_init_flags(newpwd, PDB_LOGONTIME) != PDB_DEFAULT) { + pdb_mysql_int_field(methods, &query, + config_value_write(data, + "logon time column", + CONFIG_LOGON_TIME_DEFAULT), + pdb_get_logon_time(newpwd)); + } + + if (pdb_get_init_flags(newpwd, PDB_LOGOFFTIME) != PDB_DEFAULT) { + pdb_mysql_int_field(methods, &query, + config_value_write(data, + "logoff time column", + CONFIG_LOGOFF_TIME_DEFAULT), + pdb_get_logoff_time(newpwd)); + } + + if (pdb_get_init_flags(newpwd, PDB_KICKOFFTIME) != PDB_DEFAULT) { + pdb_mysql_int_field(methods, &query, + config_value_write(data, + "kickoff time column", + CONFIG_KICKOFF_TIME_DEFAULT), + pdb_get_kickoff_time(newpwd)); + } + + if (pdb_get_init_flags(newpwd, PDB_CANCHANGETIME) != PDB_DEFAULT) { + pdb_mysql_int_field(methods, &query, + config_value_write(data, + "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_mysql_int_field(methods, &query, + config_value_write(data, + "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_mysql_int_field(methods, &query, + config_value_write(data, + "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_mysql_int_field(methods, &query, + config_value_write(data, + "hours len column", + CONFIG_HOURS_LEN_DEFAULT), + pdb_get_hours_len(newpwd)); + } + + if (pdb_get_logon_divs(newpwd)) { + pdb_mysql_int_field(methods, &query, + config_value_write(data, + "logon divs column", + CONFIG_LOGON_DIVS_DEFAULT), + pdb_get_logon_divs(newpwd)); + } + + if (pdb_get_init_flags(newpwd, PDB_UID) != PDB_DEFAULT) { + pdb_mysql_int_field(methods, &query, + config_value_write(data, "uid column", + CONFIG_UID_DEFAULT), + pdb_get_uid(newpwd)); + } + + if (pdb_get_init_flags(newpwd, PDB_GID) != PDB_DEFAULT) { + pdb_mysql_int_field(methods, &query, + config_value_write(data, "gid column", + CONFIG_GID_DEFAULT), + pdb_get_gid(newpwd)); + } + + pdb_mysql_string_field(methods, &query, + config_value_write(data, "user sid column", + CONFIG_USER_SID_DEFAULT), + sid_to_string(sid_str, + pdb_get_user_sid(newpwd))); + + pdb_mysql_string_field(methods, &query, + config_value_write(data, "group sid column", + CONFIG_GROUP_SID_DEFAULT), + sid_to_string(sid_str, + pdb_get_group_sid(newpwd))); + + pdb_mysql_string_field(methods, &query, + config_value_write(data, "username column", + CONFIG_USERNAME_DEFAULT), + pdb_get_username(newpwd)); + + pdb_mysql_string_field(methods, &query, + config_value_write(data, "domain column", + CONFIG_DOMAIN_DEFAULT), + pdb_get_domain(newpwd)); + + pdb_mysql_string_field(methods, &query, + config_value_write(data, + "nt username column", + CONFIG_NT_USERNAME_DEFAULT), + pdb_get_nt_username(newpwd)); + + pdb_mysql_string_field(methods, &query, + config_value_write(data, "fullname column", + CONFIG_FULLNAME_DEFAULT), + pdb_get_fullname(newpwd)); + + pdb_mysql_string_field(methods, &query, + config_value_write(data, + "logon script column", + CONFIG_LOGON_SCRIPT_DEFAULT), + pdb_get_logon_script(newpwd)); + + pdb_mysql_string_field(methods, &query, + config_value_write(data, + "profile path column", + CONFIG_PROFILE_PATH_DEFAULT), + pdb_get_profile_path(newpwd)); + + pdb_mysql_string_field(methods, &query, + config_value_write(data, "dir drive column", + CONFIG_DIR_DRIVE_DEFAULT), + pdb_get_dir_drive(newpwd)); + + pdb_mysql_string_field(methods, &query, + config_value_write(data, "home dir column", + CONFIG_HOME_DIR_DEFAULT), + pdb_get_homedir(newpwd)); + + pdb_mysql_string_field(methods, &query, + config_value_write(data, + "workstations column", + CONFIG_WORKSTATIONS_DEFAULT), + pdb_get_workstations(newpwd)); + + pdb_mysql_string_field(methods, &query, + config_value_write(data, + "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_mysql_string_field(methods, &query, + config_value_write(data, + "lanman pass column", + CONFIG_LM_PW_DEFAULT), temp); + + pdb_sethexpwd(temp, pdb_get_nt_passwd(newpwd), + pdb_get_acct_ctrl(newpwd)); + pdb_mysql_string_field(methods, &query, + config_value_write(data, "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(data, + "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); + } + + DEBUG(0, ("%s\n", query.part1)); + /* Execute the query */ + if (mysql_query(data->handle, query.part1)) { + DEBUG(0, + ("Error executing %s, %s\n", query.part1, + mysql_error(data->handle))); + return NT_STATUS_INVALID_PARAMETER; + } + talloc_destroy(query.mem_ctx); + 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_getgrsid(struct pdb_methods *methods, GROUP_MAP *map, + DOM_SID sid, BOOL with_priv) +{ + return get_group_map_from_sid(sid, map, with_priv) ? + NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; +} + +static NTSTATUS mysqlsam_getgrgid(struct pdb_methods *methods, GROUP_MAP *map, + gid_t gid, BOOL with_priv) +{ + return get_group_map_from_gid(gid, map, with_priv) ? + NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; +} + +static NTSTATUS mysqlsam_getgrnam(struct pdb_methods *methods, GROUP_MAP *map, + char *name, BOOL with_priv) +{ + return get_group_map_from_ntname(name, map, with_priv) ? + NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; +} + +static NTSTATUS mysqlsam_add_group_mapping_entry(struct pdb_methods *methods, + GROUP_MAP *map) +{ + return add_mapping_entry(map, TDB_INSERT) ? + NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; +} + +static NTSTATUS mysqlsam_update_group_mapping_entry(struct pdb_methods *methods, + GROUP_MAP *map) +{ + return add_mapping_entry(map, TDB_REPLACE) ? + NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; +} + +static NTSTATUS mysqlsam_delete_group_mapping_entry(struct pdb_methods *methods, + DOM_SID sid) +{ + return group_map_remove(sid) ? + NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; +} + +static NTSTATUS mysqlsam_enum_group_mapping(struct pdb_methods *methods, + enum SID_NAME_USE sid_name_use, + GROUP_MAP **rmap, int *num_entries, + BOOL unix_only, BOOL with_priv) +{ + return enum_group_mapping(sid_name_use, rmap, num_entries, unix_only, + with_priv) ? + NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; +} + + +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; + (*pdb_method)->getgrsid = mysqlsam_getgrsid; + (*pdb_method)->getgrgid = mysqlsam_getgrgid; + (*pdb_method)->getgrnam = mysqlsam_getgrnam; + (*pdb_method)->add_group_mapping_entry = mysqlsam_add_group_mapping_entry; + (*pdb_method)->update_group_mapping_entry = mysqlsam_update_group_mapping_entry; + (*pdb_method)->delete_group_mapping_entry = mysqlsam_delete_group_mapping_entry; + (*pdb_method)->enum_group_mapping = mysqlsam_enum_group_mapping; + + 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. See README 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; + } + /* 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; +} + +int init_module(void); + +int init_module() +{ + if(smb_register_passdb("mysql", mysqlsam_init, PASSDB_INTERFACE_VERSION)) + return 0; + + return 1; +} diff --git a/source4/modules/vfs_audit.c b/source4/modules/vfs_audit.c new file mode 100644 index 0000000000..b99d93d0f0 --- /dev/null +++ b/source4/modules/vfs_audit.c @@ -0,0 +1,278 @@ +/* + * Auditing VFS module for samba. Log selected file operations to syslog + * facility. + * + * Copyright (C) Tim Potter, 1999-2000 + * 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 "config.h" +#include +#include +#ifdef HAVE_UTIME_H +#include +#endif +#ifdef HAVE_DIRENT_H +#include +#endif +#include +#ifdef HAVE_FCNTL_H +#include +#endif +#include +#include +#include +#include + +#ifndef SYSLOG_FACILITY +#define SYSLOG_FACILITY LOG_USER +#endif + +#ifndef SYSLOG_PRIORITY +#define SYSLOG_PRIORITY LOG_NOTICE +#endif + +/* Function prototypes */ + +static int audit_connect(struct tcon_context *conn, const char *svc, const char *user); +static void audit_disconnect(struct tcon_context *conn); +static DIR *audit_opendir(struct tcon_context *conn, const char *fname); +static int audit_mkdir(struct tcon_context *conn, const char *path, mode_t mode); +static int audit_rmdir(struct tcon_context *conn, const char *path); +static int audit_open(struct tcon_context *conn, const char *fname, int flags, mode_t mode); +static int audit_close(struct files_struct *fsp, int fd); +static int audit_rename(struct tcon_context *conn, const char *old, const char *new); +static int audit_unlink(struct tcon_context *conn, const char *path); +static int audit_chmod(struct tcon_context *conn, const char *path, mode_t mode); +static int audit_chmod_acl(struct tcon_context *conn, const char *name, mode_t mode); +static int audit_fchmod(struct files_struct *fsp, int fd, mode_t mode); +static int audit_fchmod_acl(struct files_struct *fsp, int fd, mode_t mode); + +/* VFS operations */ + +static struct vfs_ops default_vfs_ops; /* For passthrough operation */ +static struct smb_vfs_handle_struct *audit_handle; + +static vfs_op_tuple audit_ops[] = { + + /* Disk operations */ + + {audit_connect, SMB_VFS_OP_CONNECT, SMB_VFS_LAYER_LOGGER}, + {audit_disconnect, SMB_VFS_OP_DISCONNECT, SMB_VFS_LAYER_LOGGER}, + + /* Directory operations */ + + {audit_opendir, SMB_VFS_OP_OPENDIR, SMB_VFS_LAYER_LOGGER}, + {audit_mkdir, SMB_VFS_OP_MKDIR, SMB_VFS_LAYER_LOGGER}, + {audit_rmdir, SMB_VFS_OP_RMDIR, SMB_VFS_LAYER_LOGGER}, + + /* File operations */ + + {audit_open, SMB_VFS_OP_OPEN, SMB_VFS_LAYER_LOGGER}, + {audit_close, SMB_VFS_OP_CLOSE, SMB_VFS_LAYER_LOGGER}, + {audit_rename, SMB_VFS_OP_RENAME, SMB_VFS_LAYER_LOGGER}, + {audit_unlink, SMB_VFS_OP_UNLINK, SMB_VFS_LAYER_LOGGER}, + {audit_chmod, SMB_VFS_OP_CHMOD, SMB_VFS_LAYER_LOGGER}, + {audit_fchmod, SMB_VFS_OP_FCHMOD, SMB_VFS_LAYER_LOGGER}, + {audit_chmod_acl, SMB_VFS_OP_CHMOD_ACL, SMB_VFS_LAYER_LOGGER}, + {audit_fchmod_acl, SMB_VFS_OP_FCHMOD_ACL, SMB_VFS_LAYER_LOGGER}, + + /* Finish VFS operations definition */ + + {NULL, SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP} +}; + +/* VFS initialisation function. Return vfs_op_tuple array back to SAMBA. */ + +vfs_op_tuple *vfs_init(int *vfs_version, struct vfs_ops *def_vfs_ops, + struct smb_vfs_handle_struct *vfs_handle) +{ + *vfs_version = SMB_VFS_INTERFACE_VERSION; + memcpy(&default_vfs_ops, def_vfs_ops, sizeof(struct vfs_ops)); + + audit_handle = vfs_handle; + + openlog("smbd_audit", LOG_PID, SYSLOG_FACILITY); + syslog(SYSLOG_PRIORITY, "VFS_INIT: vfs_ops loaded\n"); + return audit_ops; +} + +/* VFS finalization function. */ +void vfs_done(struct tcon_context *conn) +{ + syslog(SYSLOG_PRIORITY, "VFS_DONE: vfs module unloaded\n"); +} + +/* Implementation of vfs_ops. Pass everything on to the default + operation but log event first. */ + +static int audit_connect(struct tcon_context *conn, const char *svc, const char *user) +{ + syslog(SYSLOG_PRIORITY, "connect to service %s by user %s\n", + svc, user); + + return default_vfs_ops.connect(conn, svc, user); +} + +static void audit_disconnect(struct tcon_context *conn) +{ + syslog(SYSLOG_PRIORITY, "disconnected\n"); + default_vfs_ops.disconnect(conn); +} + +static DIR *audit_opendir(struct tcon_context *conn, const char *fname) +{ + DIR *result = default_vfs_ops.opendir(conn, fname); + + syslog(SYSLOG_PRIORITY, "opendir %s %s%s\n", + fname, + (result == NULL) ? "failed: " : "", + (result == NULL) ? strerror(errno) : ""); + + return result; +} + +static int audit_mkdir(struct tcon_context *conn, const char *path, mode_t mode) +{ + int result = default_vfs_ops.mkdir(conn, path, mode); + + syslog(SYSLOG_PRIORITY, "mkdir %s %s%s\n", + path, + (result < 0) ? "failed: " : "", + (result < 0) ? strerror(errno) : ""); + + return result; +} + +static int audit_rmdir(struct tcon_context *conn, const char *path) +{ + int result = default_vfs_ops.rmdir(conn, path); + + syslog(SYSLOG_PRIORITY, "rmdir %s %s%s\n", + path, + (result < 0) ? "failed: " : "", + (result < 0) ? strerror(errno) : ""); + + return result; +} + +static int audit_open(struct tcon_context *conn, const char *fname, int flags, mode_t mode) +{ + int result = default_vfs_ops.open(conn, fname, flags, mode); + + syslog(SYSLOG_PRIORITY, "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(struct files_struct *fsp, int fd) +{ + int result = default_vfs_ops.close(fsp, fd); + + syslog(SYSLOG_PRIORITY, "close fd %d %s%s\n", + fd, + (result < 0) ? "failed: " : "", + (result < 0) ? strerror(errno) : ""); + + return result; +} + +static int audit_rename(struct tcon_context *conn, const char *old, const char *new) +{ + int result = default_vfs_ops.rename(conn, old, new); + + syslog(SYSLOG_PRIORITY, "rename %s -> %s %s%s\n", + old, new, + (result < 0) ? "failed: " : "", + (result < 0) ? strerror(errno) : ""); + + return result; +} + +static int audit_unlink(struct tcon_context *conn, const char *path) +{ + int result = default_vfs_ops.unlink(conn, path); + + syslog(SYSLOG_PRIORITY, "unlink %s %s%s\n", + path, + (result < 0) ? "failed: " : "", + (result < 0) ? strerror(errno) : ""); + + return result; +} + +static int audit_chmod(struct tcon_context *conn, const char *path, mode_t mode) +{ + int result = default_vfs_ops.chmod(conn, path, mode); + + syslog(SYSLOG_PRIORITY, "chmod %s mode 0x%x %s%s\n", + path, mode, + (result < 0) ? "failed: " : "", + (result < 0) ? strerror(errno) : ""); + + return result; +} + +static int audit_chmod_acl(struct tcon_context *conn, const char *path, mode_t mode) +{ + int result; + + if ( !default_vfs_ops.chmod_acl ) + return 0; + + result = default_vfs_ops.chmod_acl(conn, path, mode); + + syslog(SYSLOG_PRIORITY, "chmod_acl %s mode 0x%x %s%s\n", + path, mode, + (result < 0) ? "failed: " : "", + (result < 0) ? strerror(errno) : ""); + + return result; +} + +static int audit_fchmod(struct files_struct *fsp, int fd, mode_t mode) +{ + int result = default_vfs_ops.fchmod(fsp, fd, mode); + + syslog(SYSLOG_PRIORITY, "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(struct files_struct *fsp, int fd, mode_t mode) +{ + int result; + + if ( !default_vfs_ops.fchmod_acl ) + return 0; + + result = default_vfs_ops.fchmod_acl(fsp, fd, mode); + + syslog(SYSLOG_PRIORITY, "fchmod_acl %s mode 0x%x %s%s\n", + fsp->fsp_name, mode, + (result < 0) ? "failed: " : "", + (result < 0) ? strerror(errno) : ""); + + return result; +} diff --git a/source4/modules/vfs_extd_audit.c b/source4/modules/vfs_extd_audit.c new file mode 100644 index 0000000000..e9b6ed5455 --- /dev/null +++ b/source4/modules/vfs_extd_audit.c @@ -0,0 +1,319 @@ +/* + * 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 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "config.h" +#include +#include +#ifdef HAVE_UTIME_H +#include +#endif +#ifdef HAVE_DIRENT_H +#include +#endif +#include +#ifdef HAVE_FCNTL_H +#include +#endif +#include +#include +#include +#include + +#ifndef SYSLOG_FACILITY +#define SYSLOG_FACILITY LOG_USER +#endif + +#ifndef SYSLOG_PRIORITY +#define SYSLOG_PRIORITY LOG_NOTICE +#endif + +/* Function prototypes */ + +static int audit_connect(struct tcon_context *conn, const char *svc, const char *user); +static void audit_disconnect(struct tcon_context *conn); +static DIR *audit_opendir(struct tcon_context *conn, const char *fname); +static int audit_mkdir(struct tcon_context *conn, const char *path, mode_t mode); +static int audit_rmdir(struct tcon_context *conn, const char *path); +static int audit_open(struct tcon_context *conn, const char *fname, int flags, mode_t mode); +static int audit_close(struct files_struct *fsp, int fd); +static int audit_rename(struct tcon_context *conn, const char *old, const char *new); +static int audit_unlink(struct tcon_context *conn, const char *path); +static int audit_chmod(struct tcon_context *conn, const char *path, mode_t mode); +static int audit_chmod_acl(struct tcon_context *conn, const char *name, mode_t mode); +static int audit_fchmod(struct files_struct *fsp, int fd, mode_t mode); +static int audit_fchmod_acl(struct files_struct *fsp, int fd, mode_t mode); + +/* VFS operations */ + +static struct vfs_ops default_vfs_ops; /* For passthrough operation */ +static struct smb_vfs_handle_struct *audit_handle; + +static vfs_op_tuple audit_ops[] = { + + /* Disk operations */ + + {audit_connect, SMB_VFS_OP_CONNECT, SMB_VFS_LAYER_LOGGER}, + {audit_disconnect, SMB_VFS_OP_DISCONNECT, SMB_VFS_LAYER_LOGGER}, + + /* Directory operations */ + + {audit_opendir, SMB_VFS_OP_OPENDIR, SMB_VFS_LAYER_LOGGER}, + {audit_mkdir, SMB_VFS_OP_MKDIR, SMB_VFS_LAYER_LOGGER}, + {audit_rmdir, SMB_VFS_OP_RMDIR, SMB_VFS_LAYER_LOGGER}, + + /* File operations */ + + {audit_open, SMB_VFS_OP_OPEN, SMB_VFS_LAYER_LOGGER}, + {audit_close, SMB_VFS_OP_CLOSE, SMB_VFS_LAYER_LOGGER}, + {audit_rename, SMB_VFS_OP_RENAME, SMB_VFS_LAYER_LOGGER}, + {audit_unlink, SMB_VFS_OP_UNLINK, SMB_VFS_LAYER_LOGGER}, + {audit_chmod, SMB_VFS_OP_CHMOD, SMB_VFS_LAYER_LOGGER}, + {audit_fchmod, SMB_VFS_OP_FCHMOD, SMB_VFS_LAYER_LOGGER}, + {audit_chmod_acl, SMB_VFS_OP_CHMOD_ACL, SMB_VFS_LAYER_LOGGER}, + {audit_fchmod_acl, SMB_VFS_OP_FCHMOD_ACL, SMB_VFS_LAYER_LOGGER}, + + /* Finish VFS operations definition */ + + {NULL, SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP} +}; + +/* VFS initialisation function. Return vfs_op_tuple array back to SAMBA. */ + +vfs_op_tuple *vfs_init(int *vfs_version, struct vfs_ops *def_vfs_ops, + struct smb_vfs_handle_struct *vfs_handle) +{ + *vfs_version = SMB_VFS_INTERFACE_VERSION; + memcpy(&default_vfs_ops, def_vfs_ops, sizeof(struct vfs_ops)); + + audit_handle = vfs_handle; + + openlog("smbd_audit", LOG_PID, SYSLOG_FACILITY); + syslog(SYSLOG_PRIORITY, "VFS_INIT: vfs_ops loaded\n"); + + return audit_ops; +} + +/* VFS finalization function. */ + +void vfs_done(struct tcon_context *conn) +{ + syslog(SYSLOG_PRIORITY, "VFS_DONE: vfs module unloaded\n"); +} + +/* Implementation of vfs_ops. Pass everything on to the default + operation but log event first. */ + +static int audit_connect(struct tcon_context *conn, const char *svc, const char *user) +{ + syslog(SYSLOG_PRIORITY, "connect to service %s by user %s\n", + svc, user); + DEBUG(10, ("Connected to service %s as user %s\n", + svc, user)); + + return default_vfs_ops.connect(conn, svc, user); +} + +static void audit_disconnect(struct tcon_context *conn) +{ + syslog(SYSLOG_PRIORITY, "disconnected\n"); + DEBUG(10, ("Disconnected from VFS module extd_audit\n")); + + default_vfs_ops.disconnect(conn); +} + +static DIR *audit_opendir(struct tcon_context *conn, const char *fname) +{ + DIR *result = default_vfs_ops.opendir(conn, fname); + + syslog(SYSLOG_PRIORITY, "opendir %s %s%s\n", + fname, + (result == NULL) ? "failed: " : "", + (result == NULL) ? strerror(errno) : ""); + DEBUG(1, ("vfs_extd_audit: opendir %s %s %s", + fname, + (result == NULL) ? "failed: " : "", + (result == NULL) ? strerror(errno) : "")); + + return result; +} + +static int audit_mkdir(struct tcon_context *conn, const char *path, mode_t mode) +{ + int result = default_vfs_ops.mkdir(conn, path, mode); + + syslog(SYSLOG_PRIORITY, "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(struct tcon_context *conn, const char *path) +{ + int result = default_vfs_ops.rmdir(conn, path); + + syslog(SYSLOG_PRIORITY, "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(struct tcon_context *conn, const char *fname, int flags, mode_t mode) +{ + int result = default_vfs_ops.open(conn, fname, flags, mode); + + syslog(SYSLOG_PRIORITY, "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(struct files_struct *fsp, int fd) +{ + int result = default_vfs_ops.close(fsp, fd); + + syslog(SYSLOG_PRIORITY, "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(struct tcon_context *conn, const char *old, const char *new) +{ + int result = default_vfs_ops.rename(conn, old, new); + + syslog(SYSLOG_PRIORITY, "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(struct tcon_context *conn, const char *path) +{ + int result = default_vfs_ops.unlink(conn, path); + + syslog(SYSLOG_PRIORITY, "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(struct tcon_context *conn, const char *path, mode_t mode) +{ + int result = default_vfs_ops.chmod(conn, path, mode); + + syslog(SYSLOG_PRIORITY, "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(struct tcon_context *conn, const char *path, mode_t mode) +{ + int result = default_vfs_ops.chmod_acl(conn, path, mode); + + syslog(SYSLOG_PRIORITY, "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(struct files_struct *fsp, int fd, mode_t mode) +{ + int result = default_vfs_ops.fchmod(fsp, fd, mode); + + syslog(SYSLOG_PRIORITY, "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(struct files_struct *fsp, int fd, mode_t mode) +{ + int result = default_vfs_ops.fchmod_acl(fsp, fd, mode); + + syslog(SYSLOG_PRIORITY, "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; +} diff --git a/source4/modules/vfs_fake_perms.c b/source4/modules/vfs_fake_perms.c new file mode 100644 index 0000000000..63ef66c591 --- /dev/null +++ b/source4/modules/vfs_fake_perms.c @@ -0,0 +1,471 @@ +/* + * 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 "config.h" + +#include +#include +#ifdef HAVE_UTIME_H +#include +#endif +#ifdef HAVE_DIRENT_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#include +#include + +#include +#include + +static struct vfs_ops default_vfs_ops; /* For passthrough operation */ +static struct smb_vfs_handle_struct *fake_perms_handle; /* use fake_perms_handle->data for storing per-instance private data */ + +static int fake_perms_connect(struct tcon_context *conn, const char *service, const char *user) +{ + return default_vfs_ops.connect(conn, service, user); +} + +static void fake_perms_disconnect(struct tcon_context *conn) +{ + default_vfs_ops.disconnect(conn); +} + +static SMB_BIG_UINT fake_perms_disk_free(struct tcon_context *conn, const char *path, + BOOL small_query, SMB_BIG_UINT *bsize, + SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize) +{ + return default_vfs_ops.disk_free(conn, path, small_query, bsize, + dfree, dsize); +} + +static DIR *fake_perms_opendir(struct tcon_context *conn, const char *fname) +{ + return default_vfs_ops.opendir(conn, fname); +} + +static struct dirent *fake_perms_readdir(struct tcon_context *conn, DIR *dirp) +{ + return default_vfs_ops.readdir(conn, dirp); +} + +static int fake_perms_mkdir(struct tcon_context *conn, const char *path, mode_t mode) +{ + return default_vfs_ops.mkdir(conn, path, mode); +} + +static int fake_perms_rmdir(struct tcon_context *conn, const char *path) +{ + return default_vfs_ops.rmdir(conn, path); +} + +static int fake_perms_closedir(struct tcon_context *conn, DIR *dir) +{ + return default_vfs_ops.closedir(conn, dir); +} + +static int fake_perms_open(struct tcon_context *conn, const char *fname, int flags, mode_t mode) +{ + return default_vfs_ops.open(conn, fname, flags, mode); +} + +static int fake_perms_close(struct files_struct *fsp, int fd) +{ + return default_vfs_ops.close(fsp, fd); +} + +static ssize_t fake_perms_read(struct files_struct *fsp, int fd, void *data, size_t n) +{ + return default_vfs_ops.read(fsp, fd, data, n); +} + +static ssize_t fake_perms_write(struct files_struct *fsp, int fd, const void *data, size_t n) +{ + return default_vfs_ops.write(fsp, fd, data, n); +} + +static SMB_OFF_T fake_perms_lseek(struct files_struct *fsp, int filedes, SMB_OFF_T offset, int whence) +{ + return default_vfs_ops.lseek(fsp, filedes, offset, whence); +} + +static int fake_perms_rename(struct tcon_context *conn, const char *old, const char *new) +{ + return default_vfs_ops.rename(conn, old, new); +} + +static int fake_perms_fsync(struct files_struct *fsp, int fd) +{ + return default_vfs_ops.fsync(fsp, fd); +} + +static int fake_perms_stat(struct tcon_context *conn, const char *fname, SMB_STRUCT_STAT *sbuf) +{ + int ret = default_vfs_ops.stat(conn, fname, sbuf); + 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(struct files_struct *fsp, int fd, SMB_STRUCT_STAT *sbuf) +{ + return default_vfs_ops.fstat(fsp, fd, sbuf); +} + +static int fake_perms_lstat(struct tcon_context *conn, const char *path, SMB_STRUCT_STAT *sbuf) +{ + return default_vfs_ops.lstat(conn, path, sbuf); +} + +static int fake_perms_unlink(struct tcon_context *conn, const char *path) +{ + return default_vfs_ops.unlink(conn, path); +} + +static int fake_perms_chmod(struct tcon_context *conn, const char *path, mode_t mode) +{ + return default_vfs_ops.chmod(conn, path, mode); +} + +static int fake_perms_fchmod(struct files_struct *fsp, int fd, mode_t mode) +{ + return default_vfs_ops.fchmod(fsp, fd, mode); +} + +static int fake_perms_chown(struct tcon_context *conn, const char *path, uid_t uid, gid_t gid) +{ + return default_vfs_ops.chown(conn, path, uid, gid); +} + +static int fake_perms_fchown(struct files_struct *fsp, int fd, uid_t uid, gid_t gid) +{ + return default_vfs_ops.fchown(fsp, fd, uid, gid); +} + +static int fake_perms_chdir(struct tcon_context *conn, const char *path) +{ + return default_vfs_ops.chdir(conn, path); +} + +static char *fake_perms_getwd(struct tcon_context *conn, char *buf) +{ + return default_vfs_ops.getwd(conn, buf); +} + +static int fake_perms_utime(struct tcon_context *conn, const char *path, struct utimbuf *times) +{ + return default_vfs_ops.utime(conn, path, times); +} + +static int fake_perms_ftruncate(struct files_struct *fsp, int fd, SMB_OFF_T offset) +{ + return default_vfs_ops.ftruncate(fsp, fd, offset); +} + +static BOOL fake_perms_lock(struct files_struct *fsp, int fd, int op, SMB_OFF_T offset, SMB_OFF_T count, int type) +{ + return default_vfs_ops.lock(fsp, fd, op, offset, count, type); +} + +static BOOL fake_perms_symlink(struct tcon_context *conn, const char *oldpath, const char *newpath) +{ + return default_vfs_ops.symlink(conn, oldpath, newpath); +} + +static BOOL fake_perms_readlink(struct tcon_context *conn, const char *path, char *buf, size_t bufsiz) +{ + return default_vfs_ops.readlink(conn, path, buf, bufsiz); +} + +static int fake_perms_link(struct tcon_context *conn, const char *oldpath, const char *newpath) +{ + return default_vfs_ops.link(conn, oldpath, newpath); +} + +static int fake_perms_mknod(struct tcon_context *conn, const char *path, mode_t mode, SMB_DEV_T dev) +{ + return default_vfs_ops.mknod(conn, path, mode, dev); +} + +static char *fake_perms_realpath(struct tcon_context *conn, const char *path, char *resolved_path) +{ + return default_vfs_ops.realpath(conn, path, resolved_path); +} + +static size_t fake_perms_fget_nt_acl(struct files_struct *fsp, int fd, struct security_descriptor_info **ppdesc) +{ + return default_vfs_ops.fget_nt_acl(fsp, fd, ppdesc); +} + +static size_t fake_perms_get_nt_acl(struct files_struct *fsp, const char *name, struct security_descriptor_info **ppdesc) +{ + return default_vfs_ops.get_nt_acl(fsp, name, ppdesc); +} + +static BOOL fake_perms_fset_nt_acl(struct files_struct *fsp, int fd, uint32 security_info_sent, struct security_descriptor_info *psd) +{ + return default_vfs_ops.fset_nt_acl(fsp, fd, security_info_sent, psd); +} + +static BOOL fake_perms_set_nt_acl(struct files_struct *fsp, const char *name, uint32 security_info_sent, struct security_descriptor_info *psd) +{ + return default_vfs_ops.set_nt_acl(fsp, name, security_info_sent, psd); +} + +static BOOL fake_perms_chmod_acl(struct tcon_context *conn, const char *name, mode_t mode) +{ + return default_vfs_ops.chmod_acl(conn, name, mode); +} + +static BOOL fake_perms_fchmod_acl(struct files_struct *fsp, int fd, mode_t mode) +{ + return default_vfs_ops.fchmod_acl(fsp, fd, mode); +} + +static int fake_perms_sys_acl_get_entry(struct tcon_context *conn, SMB_ACL_T theacl, int entry_id, SMB_ACL_ENTRY_T *entry_p) +{ + return default_vfs_ops.sys_acl_get_entry(conn, theacl, entry_id, entry_p); +} + +static int fake_perms_sys_acl_get_tag_type(struct tcon_context *conn, SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p) +{ + return default_vfs_ops.sys_acl_get_tag_type(conn, entry_d, tag_type_p); +} + +static int fake_perms_sys_acl_get_permset(struct tcon_context *conn, SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p) +{ + return default_vfs_ops.sys_acl_get_permset(conn, entry_d, permset_p); +} + +static void *fake_perms_sys_acl_get_qualifier(struct tcon_context *conn, SMB_ACL_ENTRY_T entry_d) +{ + return default_vfs_ops.sys_acl_get_qualifier(conn, entry_d); +} + +static SMB_ACL_T fake_perms_sys_acl_get_file(struct tcon_context *conn, const char *path_p, SMB_ACL_TYPE_T type) +{ + return default_vfs_ops.sys_acl_get_file(conn, path_p, type); +} + +static SMB_ACL_T fake_perms_sys_acl_get_fd(struct files_struct *fsp, int fd) +{ + return default_vfs_ops.sys_acl_get_fd(fsp, fd); +} + +static int fake_perms_sys_acl_clear_perms(struct tcon_context *conn, SMB_ACL_PERMSET_T permset) +{ + return default_vfs_ops.sys_acl_clear_perms(conn, permset); +} + +static int fake_perms_sys_acl_add_perm(struct tcon_context *conn, SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm) +{ + return default_vfs_ops.sys_acl_add_perm(conn, permset, perm); +} + +static char *fake_perms_sys_acl_to_text(struct tcon_context *conn, SMB_ACL_T theacl, ssize_t *plen) +{ + return default_vfs_ops.sys_acl_to_text(conn, theacl, plen); +} + +static SMB_ACL_T fake_perms_sys_acl_init(struct tcon_context *conn, int count) +{ + return default_vfs_ops.sys_acl_init(conn, count); +} + +static int fake_perms_sys_acl_create_entry(struct tcon_context *conn, SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry) +{ + return default_vfs_ops.sys_acl_create_entry(conn, pacl, pentry); +} + +static int fake_perms_sys_acl_set_tag_type(struct tcon_context *conn, SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tagtype) +{ + return default_vfs_ops.sys_acl_set_tag_type(conn, entry, tagtype); +} + +static int fake_perms_sys_acl_set_qualifier(struct tcon_context *conn, SMB_ACL_ENTRY_T entry, void *qual) +{ + return default_vfs_ops.sys_acl_set_qualifier(conn, entry, qual); +} + +static int fake_perms_sys_acl_set_permset(struct tcon_context *conn, SMB_ACL_ENTRY_T entry, SMB_ACL_PERMSET_T permset) +{ + return default_vfs_ops.sys_acl_set_permset(conn, entry, permset); +} + +static int fake_perms_sys_acl_valid(struct tcon_context *conn, SMB_ACL_T theacl ) +{ + return default_vfs_ops.sys_acl_valid(conn, theacl ); +} + +static int fake_perms_sys_acl_set_file(struct tcon_context *conn, const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl) +{ + return default_vfs_ops.sys_acl_set_file(conn, name, acltype, theacl); +} + +static int fake_perms_sys_acl_set_fd(struct files_struct *fsp, int fd, SMB_ACL_T theacl) +{ + return default_vfs_ops.sys_acl_set_fd(fsp, fd, theacl); +} + +static int fake_perms_sys_acl_delete_def_file(struct tcon_context *conn, const char *path) +{ + return default_vfs_ops.sys_acl_delete_def_file(conn, path); +} + +static int fake_perms_sys_acl_get_perm(struct tcon_context *conn, SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm) +{ + return default_vfs_ops.sys_acl_get_perm(conn, permset, perm); +} + +static int fake_perms_sys_acl_free_text(struct tcon_context *conn, char *text) +{ + return default_vfs_ops.sys_acl_free_text(conn, text); +} + +static int fake_perms_sys_acl_free_acl(struct tcon_context *conn, SMB_ACL_T posix_acl) +{ + return default_vfs_ops.sys_acl_free_acl(conn, posix_acl); +} + +static int fake_perms_sys_acl_free_qualifier(struct tcon_context *conn, void *qualifier, SMB_ACL_TAG_T tagtype) +{ + return default_vfs_ops.sys_acl_free_qualifier(conn, qualifier, tagtype); +} + + +/* VFS operations structure */ + +static vfs_op_tuple fake_perms_ops[] = { + + /* Disk operations */ + + {fake_perms_connect, SMB_VFS_OP_CONNECT, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_disconnect, SMB_VFS_OP_DISCONNECT, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_disk_free, SMB_VFS_OP_DISK_FREE, SMB_VFS_LAYER_TRANSPARENT}, + + /* Directory operations */ + + {fake_perms_opendir, SMB_VFS_OP_OPENDIR, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_readdir, SMB_VFS_OP_READDIR, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_mkdir, SMB_VFS_OP_MKDIR, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_rmdir, SMB_VFS_OP_RMDIR, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_closedir, SMB_VFS_OP_CLOSEDIR, SMB_VFS_LAYER_TRANSPARENT}, + + /* File operations */ + + {fake_perms_open, SMB_VFS_OP_OPEN, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_close, SMB_VFS_OP_CLOSE, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_read, SMB_VFS_OP_READ, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_write, SMB_VFS_OP_WRITE, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_lseek, SMB_VFS_OP_LSEEK, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_rename, SMB_VFS_OP_RENAME, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_fsync, SMB_VFS_OP_FSYNC, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_stat, SMB_VFS_OP_STAT, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_fstat, SMB_VFS_OP_FSTAT, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_lstat, SMB_VFS_OP_LSTAT, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_unlink, SMB_VFS_OP_UNLINK, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_chmod, SMB_VFS_OP_CHMOD, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_fchmod, SMB_VFS_OP_FCHMOD, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_chown, SMB_VFS_OP_CHOWN, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_fchown, SMB_VFS_OP_FCHOWN, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_chdir, SMB_VFS_OP_CHDIR, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_getwd, SMB_VFS_OP_GETWD, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_utime, SMB_VFS_OP_UTIME, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_ftruncate, SMB_VFS_OP_FTRUNCATE, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_lock, SMB_VFS_OP_LOCK, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_symlink, SMB_VFS_OP_SYMLINK, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_readlink, SMB_VFS_OP_READLINK, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_link, SMB_VFS_OP_LINK, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_mknod, SMB_VFS_OP_MKNOD, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_realpath, SMB_VFS_OP_REALPATH, SMB_VFS_LAYER_TRANSPARENT}, + + /* NT File ACL operations */ + + {fake_perms_fget_nt_acl, SMB_VFS_OP_FGET_NT_ACL, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_get_nt_acl, SMB_VFS_OP_GET_NT_ACL, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_fset_nt_acl, SMB_VFS_OP_FSET_NT_ACL, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_set_nt_acl, SMB_VFS_OP_SET_NT_ACL, SMB_VFS_LAYER_TRANSPARENT}, + + /* POSIX ACL operations */ + + {fake_perms_chmod_acl, SMB_VFS_OP_CHMOD_ACL, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_fchmod_acl, SMB_VFS_OP_FCHMOD_ACL, SMB_VFS_LAYER_TRANSPARENT}, + + {fake_perms_sys_acl_get_entry, SMB_VFS_OP_SYS_ACL_GET_ENTRY, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_sys_acl_get_tag_type, SMB_VFS_OP_SYS_ACL_GET_TAG_TYPE, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_sys_acl_get_permset, SMB_VFS_OP_SYS_ACL_GET_PERMSET, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_sys_acl_get_qualifier, SMB_VFS_OP_SYS_ACL_GET_QUALIFIER, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_sys_acl_get_file, SMB_VFS_OP_SYS_ACL_GET_FILE, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_sys_acl_get_fd, SMB_VFS_OP_SYS_ACL_GET_FD, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_sys_acl_clear_perms, SMB_VFS_OP_SYS_ACL_CLEAR_PERMS, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_sys_acl_add_perm, SMB_VFS_OP_SYS_ACL_ADD_PERM, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_sys_acl_to_text, SMB_VFS_OP_SYS_ACL_TO_TEXT, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_sys_acl_init, SMB_VFS_OP_SYS_ACL_INIT, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_sys_acl_create_entry, SMB_VFS_OP_SYS_ACL_CREATE_ENTRY, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_sys_acl_set_tag_type, SMB_VFS_OP_SYS_ACL_SET_TAG_TYPE, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_sys_acl_set_qualifier, SMB_VFS_OP_SYS_ACL_SET_QUALIFIER, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_sys_acl_set_permset, SMB_VFS_OP_SYS_ACL_SET_PERMSET, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_sys_acl_valid, SMB_VFS_OP_SYS_ACL_VALID, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_sys_acl_set_file, SMB_VFS_OP_SYS_ACL_SET_FILE, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_sys_acl_set_fd, SMB_VFS_OP_SYS_ACL_SET_FD, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_sys_acl_delete_def_file, SMB_VFS_OP_SYS_ACL_DELETE_DEF_FILE, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_sys_acl_get_perm, SMB_VFS_OP_SYS_ACL_GET_PERM, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_sys_acl_free_text, SMB_VFS_OP_SYS_ACL_FREE_TEXT, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_sys_acl_free_acl, SMB_VFS_OP_SYS_ACL_FREE_ACL, SMB_VFS_LAYER_TRANSPARENT}, + {fake_perms_sys_acl_free_qualifier, SMB_VFS_OP_SYS_ACL_FREE_QUALIFIER, SMB_VFS_LAYER_TRANSPARENT}, + + {NULL, SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP} +}; + +/* VFS initialisation - return initialized vfs_op_tuple array back to Samba */ + +vfs_op_tuple *vfs_init(int *vfs_version, struct vfs_ops *def_vfs_ops, + struct smb_vfs_handle_struct *vfs_handle) +{ + DEBUG(3, ("Initialising default vfs hooks\n")); + + *vfs_version = SMB_VFS_INTERFACE_VERSION; + memcpy(&default_vfs_ops, def_vfs_ops, sizeof(struct vfs_ops)); + + /* Remember vfs_handle for further allocation and referencing of private + information in vfs_handle->data + */ + fake_perms_handle = vfs_handle; + return fake_perms_ops; +} + +/* VFS finalization function */ +void vfs_done(struct tcon_context *conn) +{ + DEBUG(3, ("Finalizing default vfs hooks\n")); +} diff --git a/source4/modules/vfs_netatalk.c b/source4/modules/vfs_netatalk.c new file mode 100644 index 0000000000..0c1eb8d15e --- /dev/null +++ b/source4/modules/vfs_netatalk.c @@ -0,0 +1,429 @@ +/* + * AppleTalk VFS module for Samba-3.x + * + * Copyright (C) Alexei 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 "config.h" +#include +#include +#ifdef HAVE_UTIME_H +#include +#endif +#ifdef HAVE_DIRENT_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#include +#include +#include +#include + +#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 struct vfs_ops default_vfs_ops; /* For passthrough operation */ +static struct smb_vfs_handle_struct *atalk_handle; + +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 tcon_context *conn, const char *fname) +{ + DIR *ret = 0; + + ret = default_vfs_ops.opendir(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 tcon_context *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 default_vfs_ops.rmdir(conn, path); +} + +/* File operations */ + +static int atalk_rename(struct tcon_context *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 = default_vfs_ops.rename(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 tcon_context *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 = default_vfs_ops.unlink(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 tcon_context *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 = default_vfs_ops.chmod(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 tcon_context *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 = default_vfs_ops.chown(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 */ + + {atalk_opendir, SMB_VFS_OP_OPENDIR, SMB_VFS_LAYER_TRANSPARENT}, + {atalk_rmdir, SMB_VFS_OP_RMDIR, SMB_VFS_LAYER_TRANSPARENT}, + + /* File operations */ + + {atalk_rename, SMB_VFS_OP_RENAME, SMB_VFS_LAYER_TRANSPARENT}, + {atalk_unlink, SMB_VFS_OP_UNLINK, SMB_VFS_LAYER_TRANSPARENT}, + {atalk_chmod, SMB_VFS_OP_CHMOD, SMB_VFS_LAYER_TRANSPARENT}, + {atalk_chown, SMB_VFS_OP_CHOWN, SMB_VFS_LAYER_TRANSPARENT}, + + /* Finish VFS operations definition */ + + {NULL, SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP} +}; + +/* VFS initialisation function. Return vfs_op_tuple array back to SAMBA. */ +vfs_op_tuple *vfs_init(int *vfs_version, struct vfs_ops *def_vfs_ops, + struct smb_vfs_handle_struct *vfs_handle) +{ + *vfs_version = SMB_VFS_INTERFACE_VERSION; + memcpy(&default_vfs_ops, def_vfs_ops, sizeof(struct vfs_ops)); + + atalk_handle = vfs_handle; + + DEBUG(3, ("ATALK: vfs module loaded\n")); + return atalk_ops; +} + +/* VFS finalization function. */ +void vfs_done(struct tcon_context *conn) +{ + DEBUG(3, ("ATALK: vfs module unloaded\n")); +} diff --git a/source4/modules/vfs_recycle.c b/source4/modules/vfs_recycle.c new file mode 100644 index 0000000000..2a017aca04 --- /dev/null +++ b/source4/modules/vfs_recycle.c @@ -0,0 +1,648 @@ +/* + * Recycle bin VFS module for Samba. + * + * Copyright (C) 2001, Brandon Stone, Amherst College, . + * 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 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 const char *delimiter = "|"; /* delimiter for options */ + +/* One per connection */ + +typedef struct recycle_bin_struct +{ + TALLOC_CTX *mem_ctx; + char *repository; /* name of the recycle bin directory */ + BOOL keep_dir_tree; /* keep directory structure of deleted file in recycle bin */ + BOOL versions; /* create versions of deleted files with identical name */ + BOOL touch; /* touch access date of deleted file */ + char *exclude; /* which files to exclude */ + char *exclude_dir; /* which directories to exclude */ + char *noversions; /* which files to exclude from versioning */ + SMB_OFF_T maxsize; /* maximum file size to be saved */ +} recycle_bin_struct; + +typedef struct recycle_bin_connections { + int conn; + recycle_bin_struct *data; + struct recycle_bin_connections *next; +} recycle_bin_connections; + +typedef struct recycle_bin_private_data { + TALLOC_CTX *mem_ctx; + recycle_bin_connections *conns; +} recycle_bin_private_data; + +struct smb_vfs_handle_struct *recycle_bin_private_handle; + +/* VFS operations */ +static struct vfs_ops default_vfs_ops; /* For passthrough operation */ + +static int recycle_connect(struct tcon_context *conn, const char *service, const char *user); +static void recycle_disconnect(struct tcon_context *conn); +static int recycle_unlink(struct tcon_context *, const char *); + +#define VFS_OP(x) ((void *) x) + +static vfs_op_tuple recycle_ops[] = { + + /* Disk operations */ + {VFS_OP(recycle_connect), SMB_VFS_OP_CONNECT, SMB_VFS_LAYER_TRANSPARENT}, + {VFS_OP(recycle_disconnect), SMB_VFS_OP_DISCONNECT, SMB_VFS_LAYER_TRANSPARENT}, + + /* File operations */ + {VFS_OP(recycle_unlink), SMB_VFS_OP_UNLINK, SMB_VFS_LAYER_TRANSPARENT}, + + {NULL, SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP} +}; + +/** + * VFS initialisation function. + * + * @retval initialised vfs_op_tuple array + **/ +vfs_op_tuple *vfs_init(int *vfs_version, struct vfs_ops *def_vfs_ops, + struct smb_vfs_handle_struct *vfs_handle) +{ + TALLOC_CTX *mem_ctx = NULL; + + DEBUG(10, ("Initializing VFS module recycle\n")); + *vfs_version = SMB_VFS_INTERFACE_VERSION; + memcpy(&default_vfs_ops, def_vfs_ops, sizeof(struct vfs_ops)); + vfs_recycle_debug_level = debug_add_class("vfs_recycle_bin"); + 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(0, ("vfs_recycle: Debug class number of 'vfs_recycle': %d\n", vfs_recycle_debug_level)); + } + + recycle_bin_private_handle = vfs_handle; + if (!(mem_ctx = talloc_init("recycle bin data"))) { + DEBUG(0, ("Failed to allocate memory in VFS module recycle_bin\n")); + return NULL; + } + + recycle_bin_private_handle->data = talloc(mem_ctx, sizeof(recycle_bin_private_data)); + if (recycle_bin_private_handle->data == NULL) { + DEBUG(0, ("Failed to allocate memory in VFS module recycle_bin\n")); + return NULL; + } + ((recycle_bin_private_data *)(recycle_bin_private_handle->data))->mem_ctx = mem_ctx; + ((recycle_bin_private_data *)(recycle_bin_private_handle->data))->conns = NULL; + + return recycle_ops; +} + +/** + * VFS finalization function. + * + **/ +void vfs_done(void) +{ + recycle_bin_private_data *recdata; + recycle_bin_connections *recconn; + + DEBUG(10, ("Unloading/Cleaning VFS module recycle bin\n")); + + if (recycle_bin_private_handle) + recdata = (recycle_bin_private_data *)(recycle_bin_private_handle->data); + else { + DEBUG(0, ("Recycle bin not initialized!\n")); + return; + } + + if (recdata) { + if (recdata->conns) { + recconn = recdata->conns; + while (recconn) { + talloc_destroy(recconn->data->mem_ctx); + recconn = recconn->next; + } + } + if (recdata->mem_ctx) { + talloc_destroy(recdata->mem_ctx); + } + recdata = NULL; + } +} + +static int recycle_connect(struct tcon_context *conn, const char *service, const char *user) +{ + TALLOC_CTX *ctx = NULL; + recycle_bin_struct *recbin; + recycle_bin_connections *recconn; + recycle_bin_connections *recconnbase; + recycle_bin_private_data *recdata; + char *tmp_str; + + DEBUG(10, ("Called for service %s (%d) as user %s\n", service, SNUM(conn), user)); + + if (recycle_bin_private_handle) + recdata = (recycle_bin_private_data *)(recycle_bin_private_handle->data); + else { + DEBUG(0, ("Recycle bin not initialized!\n")); + return -1; + } + + if (!(ctx = talloc_init("recycle bin connection"))) { + DEBUG(0, ("Failed to allocate memory in VFS module recycle_bin\n")); + return -1; + } + + recbin = talloc(ctx, sizeof(recycle_bin_struct)); + if (recbin == NULL) { + DEBUG(0, ("Failed to allocate memory in VFS module recycle_bin\n")); + return -1; + } + recbin->mem_ctx = ctx; + + /* Set defaults */ + recbin->repository = talloc_strdup(recbin->mem_ctx, ".recycle"); + ALLOC_CHECK(recbin->repository, error); + recbin->keep_dir_tree = False; + recbin->versions = False; + recbin->touch = False; + recbin->exclude = ""; + recbin->exclude_dir = ""; + recbin->noversions = ""; + recbin->maxsize = 0; + + /* parse configuration options */ + if ((tmp_str = lp_parm_string(SNUM(conn), "vfs_recycle_bin", "repository")) != NULL) { + recbin->repository = talloc_sub_conn(recbin->mem_ctx, conn, tmp_str); + ALLOC_CHECK(recbin->repository, error); + trim_string(recbin->repository, "/", "/"); + DEBUG(5, ("recycle.bin: repository = %s\n", recbin->repository)); + } + + recbin->keep_dir_tree = lp_parm_bool(SNUM(conn), "vfs_recycle_bin", "keeptree", False); + DEBUG(5, ("recycle.bin: keeptree = %d\n", recbin->keep_dir_tree)); + + recbin->versions = lp_parm_bool(SNUM(conn), "vfs_recycle_bin", "versions", False); + DEBUG(5, ("recycle.bin: versions = %d\n", recbin->versions)); + + recbin->touch = lp_parm_bool(SNUM(conn), "vfs_recycle_bin", "touch", False); + DEBUG(5, ("recycle.bin: touch = %d\n", recbin->touch)); + + recbin->maxsize = lp_parm_ulong(SNUM(conn), "vfs_recycle_bin", "maxsize"); + if (recbin->maxsize == 0) { + recbin->maxsize = -1; + DEBUG(5, ("recycle.bin: maxsize = -infinite-\n")); + } else { + DEBUG(5, ("recycle.bin: maxsize = %ld\n", (long int)recbin->maxsize)); + } + + if ((tmp_str = lp_parm_string(SNUM(conn), "vfs_recycle_bin", "exclude")) != NULL) { + recbin->exclude = talloc_strdup(recbin->mem_ctx, tmp_str); + ALLOC_CHECK(recbin->exclude, error); + DEBUG(5, ("recycle.bin: exclude = %s\n", recbin->exclude)); + } + if ((tmp_str = lp_parm_string(SNUM(conn), "vfs_recycle_bin", "exclude_dir")) != NULL) { + recbin->exclude_dir = talloc_strdup(recbin->mem_ctx, tmp_str); + ALLOC_CHECK(recbin->exclude_dir, error); + DEBUG(5, ("recycle.bin: exclude_dir = %s\n", recbin->exclude_dir)); + } + if ((tmp_str = lp_parm_string(SNUM(conn), "vfs_recycle_bin", "noversions")) != NULL) { + recbin->noversions = talloc_strdup(recbin->mem_ctx, tmp_str); + ALLOC_CHECK(recbin->noversions, error); + DEBUG(5, ("recycle.bin: noversions = %s\n", recbin->noversions)); + } + + recconn = talloc(recdata->mem_ctx, sizeof(recycle_bin_connections)); + if (recconn == NULL) { + DEBUG(0, ("Failed to allocate memory in VFS module recycle_bin\n")); + goto error; + } + recconn->conn = SNUM(conn); + recconn->data = recbin; + recconn->next = NULL; + if (recdata->conns) { + recconnbase = recdata->conns; + while (recconnbase->next != NULL) recconnbase = recconnbase->next; + recconnbase->next = recconn; + } else { + recdata->conns = recconn; + } + return default_vfs_ops.connect(conn, service, user); + +error: + talloc_destroy(ctx); + return -1; +} + +static void recycle_disconnect(struct tcon_context *conn) +{ + recycle_bin_private_data *recdata; + recycle_bin_connections *recconn; + + DEBUG(10, ("Disconnecting VFS module recycle bin\n")); + + if (recycle_bin_private_handle) + recdata = (recycle_bin_private_data *)(recycle_bin_private_handle->data); + else { + DEBUG(0, ("Recycle bin not initialized!\n")); + return; + } + + if (recdata) { + if (recdata->conns) { + if (recdata->conns->conn == SNUM(conn)) { + talloc_destroy(recdata->conns->data->mem_ctx); + recdata->conns = recdata->conns->next; + } else { + recconn = recdata->conns; + while (recconn->next) { + if (recconn->next->conn == SNUM(conn)) { + talloc_destroy(recconn->next->data->mem_ctx); + recconn->next = recconn->next->next; + break; + } + recconn = recconn->next; + } + } + } + } + default_vfs_ops.disconnect(conn); +} + +static BOOL recycle_directory_exist(struct tcon_context *conn, const char *dname) +{ + SMB_STRUCT_STAT st; + + if (default_vfs_ops.stat(conn, dname, &st) == 0) { + if (S_ISDIR(st.st_mode)) { + return True; + } + } + + return False; +} + +static BOOL recycle_file_exist(struct tcon_context *conn, const char *fname) +{ + SMB_STRUCT_STAT st; + + if (default_vfs_ops.stat(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(struct tcon_context *conn, const char *fname) +{ + SMB_STRUCT_STAT st; + if (default_vfs_ops.stat(conn, fname, &st) != 0) { + DEBUG(0,("recycle.bin: 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(struct tcon_context *conn, 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_IREAD | S_IWRITE | S_IEXEC; + + tmp_str = strdup(dname); + ALLOC_CHECK(tmp_str, done); + tok_str = tmp_str; + + len = strlen(dname); + 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(conn, new_dir)) + DEBUG(10, ("recycle.bin: dir %s already exists\n", new_dir)); + else { + DEBUG(5, ("recycle.bin: creating new dir %s\n", new_dir)); + if (default_vfs_ops.mkdir(conn, new_dir, mode) != 0) { + DEBUG(1,("recycle.bin: 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, const char *needle) +{ + char *token; + char *tok_str; + char *tmp_str; + BOOL ret = False; + + if (haystack == NULL || strlen(haystack) == 0 || needle == NULL || strlen(needle) == 0) { + return False; + } + + tmp_str = strdup(haystack); + ALLOC_CHECK(tmp_str, done); + token = tok_str = tmp_str; + + for(token = strtok(tok_str, delimiter); token; token = strtok(NULL, delimiter)) { + if(strcmp(token, needle) == 0) { + ret = True; + goto done; + } + } +done: + SAFE_FREE(tmp_str); + return ret; +} + +/** + * 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, const char *needle) +{ + char *token; + char *tok_str; + char *tmp_str; + BOOL ret = False; + + if (haystack == NULL || strlen(haystack) == 0 || needle == NULL || strlen(needle) == 0) { + return False; + } + + tmp_str = strdup(haystack); + ALLOC_CHECK(tmp_str, done); + token = tok_str = tmp_str; + + for(token = strtok(tok_str, delimiter); token; token = strtok(NULL, delimiter)) { + if (!unix_wild_match(token, needle)) { + ret = True; + goto done; + } + } +done: + SAFE_FREE(tmp_str); + return ret; +} + +/** + * Touch access date + **/ +static void recycle_touch(struct tcon_context *conn, const char *fname) +{ + SMB_STRUCT_STAT st; + struct utimbuf tb; + time_t currtime; + + if (default_vfs_ops.stat(conn, fname, &st) != 0) { + DEBUG(0,("recycle.bin: stat for %s returned %s\n", fname, strerror(errno))); + return; + } + currtime = time(&currtime); + tb.actime = currtime; + tb.modtime = st.st_mtime; + + if (default_vfs_ops.utime(conn, fname, &tb) == -1 ) + DEBUG(0, ("recycle.bin: touching %s failed, reason = %s\n", fname, strerror(errno))); + } + +/** + * Check if file should be recycled + **/ +static int recycle_unlink(struct tcon_context *conn, const char *file_name) +{ + recycle_bin_private_data *recdata; + recycle_bin_connections *recconn; + recycle_bin_struct *recbin; + char *path_name = NULL; + char *temp_name = NULL; + char *final_name = NULL; + const char *base; + int i; +/* SMB_BIG_UINT dfree, dsize, bsize; */ + SMB_OFF_T file_size; /* space_avail; */ + BOOL exist; + int rc = -1; + + recbin = NULL; + if (recycle_bin_private_handle) { + recdata = (recycle_bin_private_data *)(recycle_bin_private_handle->data); + if (recdata) { + if (recdata->conns) { + recconn = recdata->conns; + while (recconn && recconn->conn != SNUM(conn)) recconn = recconn->next; + if (recconn != NULL) { + recbin = recconn->data; + } + } + } + } + if (recbin == NULL) { + DEBUG(0, ("Recycle bin not initialized!\n")); + rc = default_vfs_ops.unlink(conn, file_name); + goto done; + } + + if(!recbin->repository || *(recbin->repository) == '\0') { + DEBUG(3, ("Recycle path not set, purging %s...\n", file_name)); + rc = default_vfs_ops.unlink(conn, file_name); + goto done; + } + + /* we don't recycle the recycle bin... */ + if (strncmp(file_name, recbin->repository, strlen(recbin->repository)) == 0) { + DEBUG(3, ("File is within recycling bin, unlinking ...\n")); + rc = default_vfs_ops.unlink(conn, file_name); + goto done; + } + + file_size = recycle_get_file_size(conn, file_name); + /* it is wrong to purge filenames only because they are empty imho + * --- simo + * + if(fsize == 0) { + DEBUG(3, ("File %s is empty, purging...\n", file_name)); + rc = default_vfs_ops.unlink(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 + */ + if(recbin->maxsize > 0 && file_size > recbin->maxsize) { + DEBUG(3, ("File %s exceeds maximum recycle size, purging... \n", file_name)); + rc = default_vfs_ops.unlink(conn, file_name); + goto done; + } + + /* FIXME: this is wrong: moving files with rename does not change the disk space + * allocation + * + space_avail = default_vfs_ops.disk_free(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, ("Not enough diskspace, purging file %s\n", file_name)); + rc = default_vfs_ops.unlink(conn, file_name); + goto done; + } + */ + + /* extract filename and path */ + path_name = (char *)malloc(PATH_MAX); + ALLOC_CHECK(path_name, done); + *path_name = '\0'; + safe_strcpy(path_name, file_name, PATH_MAX - 1); + base = strrchr(path_name, '/'); + if (base == NULL) { + base = file_name; + safe_strcpy(path_name, "/", PATH_MAX - 1); + } + else { + base++; + } + + DEBUG(10, ("recycle.bin: fname = %s\n", file_name)); /* original filename with path */ + DEBUG(10, ("recycle.bin: fpath = %s\n", path_name)); /* original path */ + DEBUG(10, ("recycle.bin: base = %s\n", base)); /* filename without path */ + + if (matchparam(recbin->exclude, base)) { + DEBUG(3, ("recycle.bin: file %s is excluded \n", base)); + rc = default_vfs_ops.unlink(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(recbin->exclude_dir, path_name)) { + DEBUG(3, ("recycle.bin: directory %s is excluded \n", path_name)); + rc = default_vfs_ops.unlink(conn, file_name); + goto done; + } + + temp_name = (char *)strdup(recbin->repository); + ALLOC_CHECK(temp_name, done); + + /* see if we need to recreate the original directory structure in the recycle bin */ + if (recbin->keep_dir_tree == True) { + safe_strcat(temp_name, "/", PATH_MAX - 1); + safe_strcat(temp_name, path_name, PATH_MAX - 1); + } + + exist = recycle_directory_exist(conn, temp_name); + if (exist) { + DEBUG(10, ("recycle.bin: Directory already exists\n")); + } else { + DEBUG(10, ("recycle.bin: Creating directory %s\n", temp_name)); + if (recycle_create_dir(conn, temp_name) == False) { + DEBUG(3, ("Could not create directory, purging %s...\n", file_name)); + rc = default_vfs_ops.unlink(conn, file_name); + goto done; + } + } + + final_name = NULL; + asprintf(&final_name, "%s/%s", temp_name, base); + ALLOC_CHECK(final_name, done); + DEBUG(10, ("recycle.bin: recycled file name%s\n", temp_name)); /* new filename with path */ + + /* check if we should delete file from recycle bin */ + if (recycle_file_exist(conn, final_name)) { + if (recbin->versions == False || matchparam(recbin->noversions, base) == True) { + DEBUG(3, ("recycle.bin: Removing old file %s from recycle bin\n", final_name)); + if (default_vfs_ops.unlink(conn, final_name) != 0) { + DEBUG(1, ("recycle.bin: Error deleting old file: %s\n", strerror(errno))); + } + } + } + + /* rename file we move to recycle bin */ + i = 1; + while (recycle_file_exist(conn, final_name)) { + snprintf(final_name, PATH_MAX, "%s/Copy #%d of %s", temp_name, i++, base); + } + + DEBUG(10, ("recycle.bin: Moving %s to %s\n", file_name, final_name)); + rc = default_vfs_ops.rename(conn, file_name, final_name); + if (rc != 0) { + DEBUG(3, ("recycle.bin: Move error %d (%s), purging file %s (%s)\n", errno, strerror(errno), file_name, final_name)); + rc = default_vfs_ops.unlink(conn, file_name); + goto done; + } + + /* touch access date of moved file */ + if (recbin->touch == True ) + recycle_touch(conn, final_name); + +done: + SAFE_FREE(path_name); + SAFE_FREE(temp_name); + SAFE_FREE(final_name); + return rc; +} diff --git a/source4/modules/xml.c b/source4/modules/xml.c new file mode 100644 index 0000000000..ead3e3a874 --- /dev/null +++ b/source4/modules/xml.c @@ -0,0 +1,575 @@ + +/* + * 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://www.samba.org/ns" + +#include "includes.h" + +#include +#include + +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; +} + +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; +} + +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); + } + tmp = xmlGetProp(cur, "uid"); + if (tmp) + pdb_set_uid(u, atol(tmp), 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, "gid"); + if (tmp) + pdb_set_gid(u, atol(tmp), PDB_SET); + 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, "unknown_3") && cur->ns == ns) + pdb_set_unknown_3(u, + atol(xmlNodeListGetString + (doc, cur->xmlChildrenNode, 1)), PDB_SET); + + else if (!strcmp(cur->name, "unknown_5") && cur->ns == ns) + pdb_set_unknown_5(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; + +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_init_flags(u, PDB_UID) != PDB_DEFAULT) + xmlNewProp(user, "uid", iota(pdb_get_uid(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_GID) != PDB_DEFAULT) + xmlNewProp(cur, "gid", iota(pdb_get_gid(u))); + + if (pdb_get_init_flags(u, PDB_LOGONTIME) != PDB_DEFAULT) + xmlNewChild(user, data->ns, "login_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))); + xmlNewChild(user, data->ns, "unknown_3", iota(pdb_get_unknown_3(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, "unknown_5", iota(pdb_get_unknown_5(u))); + xmlNewChild(user, data->ns, "unknown_6", iota(pdb_get_unknown_6(u))); + xmlSaveFile(data->location, data->doc); + + return NT_STATUS_OK; +} + +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)->getgrsid = NULL; + (*pdb_method)->getgrgid = NULL; + (*pdb_method)->getgrnam = NULL; + (*pdb_method)->add_group_mapping_entry = NULL; + (*pdb_method)->update_group_mapping_entry = NULL; + (*pdb_method)->delete_group_mapping_entry = NULL; + (*pdb_method)->enum_group_mapping = NULL; + + data = talloc(pdb_context->mem_ctx, sizeof(pdb_xml)); + data->location = + (location ? talloc_strdup(pdb_context->mem_ctx, location) : "-"); + data->pwent = NULL; + data->written = 0; + (*pdb_method)->private_data = data; + + LIBXML_TEST_VERSION xmlKeepBlanksDefault(0); + + return NT_STATUS_OK; +} + +int init_module(void); + +int init_module() +{ + if(smb_register_passdb("xml", xmlsam_init, PASSDB_INTERFACE_VERSION)) + return 0; + + return 1; +} diff --git a/source4/msdfs/README b/source4/msdfs/README new file mode 100644 index 0000000000..0e924b31dc --- /dev/null +++ b/source4/msdfs/README @@ -0,0 +1,32 @@ +Setting up MS Dfs in Samba +kalele@veritas.com March 2000 + +Currently, MS Dfs support is a configure time parameter (--with-msdfs). Can be changed later to always compile it in.. + +To have a server announce itself as a Dfs server, add a "host msdfs=yes" entry to smb.conf. + +To make a share a Dfs root, add a "msdfs root=yes" entry to the share definition +in the smb.conf file. +e.g. +[pub] + path = /export/publicsmb + msdfs root = yes + +To create dfs volumes/junctions in the share, create symbolic links of the +format msdfs:server1\share1,server2\share2 and so on. + +In the above example, create a dfs volume "dfsstorage" in the [pub] share as: +cd /export/publicsmb +ln -s msdfs:serverA\\share dfsstorage + +Clicking on dfsstorage from a dfs-aware client will show you the contents of +\\serverA\share + +Shares with "msdfs root = no" (which is the default) entries are served as normal +shares and the client stops talking Dfs with Samba after a tconX. + +NOTES: +* Windows clients need to be rebooted if a non-dfs root is made a dfs root or + vice versa. A better option is to introduce a new share and make it the dfs root. +* Currently there's a restriction that msdfs symlink names should be all + lowercase. diff --git a/source4/msdfs/msdfs.c b/source4/msdfs/msdfs.c new file mode 100644 index 0000000000..ac9566fea9 --- /dev/null +++ b/source4/msdfs/msdfs.c @@ -0,0 +1,913 @@ +/* + 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; +#ifdef 1 +static uint32 dfs_referral_tries = 0; +#endif + +/********************************************************************** + Parse the pathname of the form \hostname\service\reqpath + into the dfs_path structure + **********************************************************************/ + +static BOOL parse_dfs_path(const char *pathname, struct dfs_path* pdp) +{ + pstring pathname_local; + char* p,*temp; + + pstrcpy(pathname_local,pathname); + p = temp = pathname_local; + + ZERO_STRUCTP(pdp); + + trim_string(temp,"\\","\\"); + DEBUG(10,("temp in parse_dfs_path: .%s. after trimming \\'s\n",temp)); + + /* now tokenize */ + /* parse out hostname */ + p = strchr(temp,'\\'); + if(p == NULL) + return False; + *p = '\0'; + pstrcpy(pdp->hostname,temp); + DEBUG(10,("hostname: %s\n",pdp->hostname)); + + /* parse out servicename */ + temp = p+1; + p = strchr(temp,'\\'); + if(p == NULL) { + pstrcpy(pdp->servicename,temp); + pdp->reqpath[0] = '\0'; + return True; + } + *p = '\0'; + pstrcpy(pdp->servicename,temp); + DEBUG(10,("servicename: %s\n",pdp->servicename)); + + /* rest is reqpath */ + pstrcpy(pdp->reqpath, p+1); + p = pdp->reqpath; + while (*p) { + if (*p == '\\') *p = '/'; + p++; + } + + DEBUG(10,("rest of the path: %s\n",pdp->reqpath)); + return True; +} + +/******************************************************** + Fake up a connection struct for the VFS layer. +*********************************************************/ + +static BOOL create_conn_struct( struct tcon_context *conn, int snum, char *path) +{ + ZERO_STRUCTP(conn); + conn->service = snum; + conn->connectpath = path; + pstring_sub(conn->connectpath , "%S", lp_servicename(snum)); + + if (!smbd_vfs_init(conn)) { + DEBUG(0,("create_conn_struct: smbd_vfs_init failed.\n")); + 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) && countvfs_ops.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 = conn->vfs_ops.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(char* dfspath, struct dfs_path* dp, + struct tcon_context *conn, + BOOL findfirst_flag, + struct referral** reflistpp, int* refcntp, + BOOL* self_referralp, int* consumedcntp) +{ + fstring localpath; + + char *p; + fstring reqpath; + + if (!dp || !conn) { + DEBUG(1,("resolve_dfs_path: NULL dfs_path* or NULL tcon_context!\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; + } + + /* check if need to redirect */ + fstrcpy(localpath, conn->connectpath); + fstrcat(localpath, "/"); + fstrcat(localpath, dp->reqpath); + 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; + } + } + + /* also redirect if the parent directory is a dfs link */ + fstrcpy(reqpath, dp->reqpath); + p = strrchr(reqpath, '/'); + if (p) { + *p = '\0'; + fstrcpy(localpath, conn->connectpath); + fstrcat(localpath, "/"); + fstrcat(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_string(buf, NULL, "\\"); + q = strrchr(buf, '\\'); + if (q) + *q = '\0'; + *consumedcntp = strlen(buf); + DEBUG(10, ("resolve_dfs_path: Path consumed: %d\n", *consumedcntp)); + } + + return True; + } + } + + 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(const char *pathname, struct tcon_context *conn, + BOOL findfirst_flag) +{ + struct dfs_path dp; + + if (!conn || !pathname) + return False; + + parse_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))) { + fstrcpy(pathname, dp.reqpath); + return False; + } + + if (strcasecmp(dp.servicename, lp_servicename(SNUM(conn)) ) != 0) + 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 */ + fstrcpy(pathname, dp.reqpath); + DEBUG(3,("dfs_redirect: Path converted to non-dfs path %s\n", + pathname)); + return False; + } + /* never reached */ + return False; +} + +/********************************************************************** + Gets valid referrals for a dfs path and fills up the + junction_map structure + **********************************************************************/ +BOOL get_referred_path(char *pathname, struct junction_map* jn, + int* consumedcntp, BOOL* self_referralp) +{ + struct dfs_path dp; + + struct tcon_context conns; + struct tcon_context *conn = &conns; + pstring conn_path; + int snum; + + BOOL self_referral = False; + + if (!pathname || !jn) + return False; + + if (self_referralp) + *self_referralp = False; + else + self_referralp = &self_referral; + + parse_dfs_path(pathname, &dp); + + /* Verify hostname in path */ + if (local_machine && (strcasecmp(local_machine, dp.hostname) != 0)) { + + /* 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(jn->service_name, dp.servicename); + pstrcpy(jn->volume_name, dp.reqpath); + + /* Verify the share is a dfs root */ + snum = lp_servicenumber(jn->service_name); + if(snum < 0) { + if ((snum = find_service(jn->service_name)) < 0) + return False; + } + + pstrcpy(conn_path, lp_pathname(snum)); + if (!create_conn_struct(conn, snum, conn_path)) + return False; + + if (!lp_msdfs_root(SNUM(conn))) { + DEBUG(3,("get_referred_path: .%s. in dfs path %s is not a dfs root.\n", + dp.servicename, pathname)); + return False; + } + + if (*lp_msdfs_proxy(snum) != '\0') { + struct referral* ref; + jn->referral_count = 1; + if ((ref = (struct referral*) malloc(sizeof(struct referral))) + == NULL) { + DEBUG(0, ("malloc failed for referral\n")); + return False; + } + + 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; + jn->referral_list = ref; + if (consumedcntp) + *consumedcntp = strlen(pathname); + return True; + } + + /* If not remote & not a self referral, return False */ + if (!resolve_dfs_path(pathname, &dp, conn, False, + &jn->referral_list, &jn->referral_count, + self_referralp, consumedcntp)) { + if (!*self_referralp) { + DEBUG(3,("get_referred_path: No valid referrals for path %s\n", pathname)); + return False; + } + } + + /* if self_referral, fill up the junction map */ + if (*self_referralp) { + struct referral* ref; + jn->referral_count = 1; + if((ref = (struct referral*) malloc(sizeof(struct referral))) + == NULL) { + DEBUG(0,("malloc failed for referral\n")); + return False; + } + + pstrcpy(ref->alternate_path,pathname); + ref->proximity = 0; + ref->ttl = REFERRAL_TTL; + jn->referral_list = ref; + if (consumedcntp) + *consumedcntp = strlen(pathname); + } + + return True; +} + +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;ireferral_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;ireferral_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;ireferral_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;ireferral_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(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 (strlen(pathnamep) > 1 && pathnamep[0] == '\\' + && pathnamep[1] == '\\') + pathnamep++; + + pstrcpy(buf, pathnamep); + if (!get_referred_path(buf, &junction, &consumedcnt, + &self_referral)) + return -1; + + 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;i3) + 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* jn) +{ + struct dfs_path dp; + + parse_dfs_path(pathname,&dp); + + /* check if path is dfs : validate first token */ + if (local_machine && (strcasecmp(local_machine,dp.hostname)!=0)) { + + /* 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(jn->service_name,dp.servicename); + pstrcpy(jn->volume_name,dp.reqpath); + return True; +} + +/********************************************************************** + Forms a valid Unix pathname from the junction + **********************************************************************/ + +static BOOL junction_to_local_path(struct junction_map* jn, char* path, + int max_pathlen, struct tcon_context *conn) +{ + int snum; + pstring conn_path; + + if(!path || !jn) + return False; + + snum = lp_servicenumber(jn->service_name); + if(snum < 0) + return False; + + safe_strcpy(path, lp_pathname(snum), max_pathlen-1); + safe_strcat(path, "/", max_pathlen-1); + strlower(jn->volume_name); + safe_strcat(path, jn->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* jn, BOOL exists) +{ + pstring path; + pstring msdfs_link; + struct tcon_context conns; + struct tcon_context *conn = &conns; + int i=0; + BOOL insert_comma = False; + + if(!junction_to_local_path(jn, path, sizeof(path), conn)) + return False; + + /* form the msdfs_link contents */ + pstrcpy(msdfs_link, "msdfs:"); + for(i=0; ireferral_count; i++) { + char* refpath = jn->referral_list[i].alternate_path; + + trim_string(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(conn->vfs_ops.unlink(conn,path)!=0) + return False; + + if(conn->vfs_ops.symlink(conn, msdfs_link, path) < 0) { + DEBUG(1,("create_msdfs_link: symlink failed %s -> %s\nError: %s\n", + path, msdfs_link, strerror(errno))); + return False; + } + return True; +} + +BOOL remove_msdfs_link(struct junction_map* jn) +{ + pstring path; + struct tcon_context conns; + struct tcon_context *conn = &conns; + + if(!junction_to_local_path(jn, path, sizeof(path), conn)) + return False; + + if(conn->vfs_ops.unlink(conn, path)!=0) + return False; + + return True; +} + +static BOOL form_junctions(int snum, struct junction_map* jn, int* jn_count) +{ + int cnt = *jn_count; + DIR *dirp; + char* dname; + pstring connect_path; + char* service_name = lp_servicename(snum); + struct tcon_context conns; + struct tcon_context *conn = &conns; + struct referral *ref = NULL; + + 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(jn[cnt].service_name, service_name); + jn[cnt].volume_name[0] = '\0'; + jn[cnt].referral_count = 1; + + ref = jn[cnt].referral_list + = (struct referral*) malloc(sizeof(struct referral)); + if (jn[cnt].referral_list == NULL) { + DEBUG(0, ("Malloc failed!\n")); + return False; + } + + ref->proximity = 0; + ref->ttl = REFERRAL_TTL; + if (*lp_msdfs_proxy(snum) != '\0') { + pstrcpy(ref->alternate_path, lp_msdfs_proxy(snum)); + *jn_count = ++cnt; + return True; + } + + slprintf(ref->alternate_path, sizeof(pstring)-1, + "\\\\%s\\%s", local_machine, service_name); + cnt++; + + /* Now enumerate all dfs links */ + dirp = conn->vfs_ops.opendir(conn, connect_path); + if(!dirp) + return False; + + while((dname = vfs_readdirname(conn, dirp)) != NULL) { + pstring pathreal; + + pstrcpy(pathreal, connect_path); + pstrcat(pathreal, "/"); + pstrcat(pathreal, dname); + + if (is_msdfs_link(conn, pathreal, &(jn[cnt].referral_list), + &(jn[cnt].referral_count), NULL)) { + pstrcpy(jn[cnt].service_name, service_name); + pstrcpy(jn[cnt].volume_name, dname); + cnt++; + } + } + + conn->vfs_ops.closedir(conn,dirp); + *jn_count = cnt; + return True; +} + +int enum_msdfs_links(struct junction_map* jn) +{ + int i=0; + int jn_count = 0; + + if(!lp_host_msdfs()) + return -1; + + for(i=0;*lp_servicename(i);i++) { + if(lp_msdfs_root(i)) + form_junctions(i,jn,&jn_count); + } + return jn_count; +} + diff --git a/source4/nmbd/.cvsignore b/source4/nmbd/.cvsignore new file mode 100644 index 0000000000..e69de29bb2 diff --git a/source4/nmbd/asyncdns.c b/source4/nmbd/asyncdns.c new file mode 100644 index 0000000000..c86ee69a09 --- /dev/null +++ b/source4/nmbd/asyncdns.c @@ -0,0 +1,349 @@ +/* + 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; + char *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; + fstring qname; + + DEBUGLEVEL = -1; + + while (1) { + if (read_data(fd_in, (char *)&r, sizeof(r)) != sizeof(r)) + break; + + fstrcpy(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) +{ + char *qname = question->name; + struct in_addr dns_ip; + + 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/source4/nmbd/nmbd.c b/source4/nmbd/nmbd.c new file mode 100644 index 0000000000..2b7d8033a2 --- /dev/null +++ b/source4/nmbd/nmbd.c @@ -0,0 +1,778 @@ +/* + 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 (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 = False; + +/* fork or run in foreground ? */ +static BOOL Fork = True; + +/* log to standard output ? */ +static BOOL log_stdout = False; + +/* 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(); +} + +/******************************************************************* + Print out all talloc memory info. +********************************************************************/ + +void return_all_talloc_info(int msg_type, pid_t src_pid, void *buf, size_t len) +{ + TALLOC_CTX *ctx = talloc_init("info context"); + char *info = NULL; + + if (!ctx) + return; + + info = talloc_describe_all(ctx); + if (info) + DEBUG(10,(info)); + message_send_pid(src_pid, MSG_TALLOC_USAGE, info, info ? strlen(info) + 1 : 0, True); + talloc_destroy(ctx); +} + +#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"); + + 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); +} + +/**************************************************************************** ** + The main select loop. + **************************************************************************** */ + +static void process(void) +{ + BOOL run_election; + + 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" ) ); + write_browse_list( 0, True ); + dump_all_namelists(); + reload_nmbd_services( True ); + reopen_logs(); + if(reload_interfaces(0)) + 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,0,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[]) +{ + static BOOL opt_interactive = False; + 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, &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" }, + {NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_debug }, + {NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_configfile }, + {NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_socket_options }, + {NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_version }, + {NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_netbios_name }, + {NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_log_base }, + { NULL } + }; + int opt; + pstring logfile; + + global_nmb_port = NMB_PORT; + 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 recieve 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 + pc = poptGetContext("nmbd", argc, argv, long_options, 0); + + while((opt = poptGetNextOpt(pc)) != -1) + { } + + poptFreeContext(pc); + + 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", VERSION ) ); + DEBUGADD( 0, ( "Copyright Andrew Tridgell and the Samba Team 1994-2002\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_REQ_TALLOC_USAGE, return_all_talloc_info); + + 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/source4/nmbd/nmbd_become_dmb.c b/source4/nmbd/nmbd_become_dmb.c new file mode 100644 index 0000000000..d8122777fe --- /dev/null +++ b/source4/nmbd/nmbd_become_dmb.c @@ -0,0 +1,396 @@ +/* + 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 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) +{ + struct work_record *work = find_workgroup_on_subnet(subrec, fail_name->name); + struct server_record *servrec; + + if(!work) + { + DEBUG(0,("become_domain_master_fail: Error - cannot find \ +workgroup %s on subnet %s\n", fail_name->name, subrec->subnet_name)); + return; + } + + /* Set the state back to DOMAIN_NONE. */ + work->dom_state = DOMAIN_NONE; + + if((servrec = find_server_in_workgroup( work, lp_netbios_name())) == NULL) + { + DEBUG(0,("become_domain_master_fail: Error - cannot find server %s \ +in workgroup %s on subnet %s\n", + lp_netbios_name(), 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) +{ + struct work_record *work = find_workgroup_on_subnet( subrec, registered_name->name); + struct server_record *servrec; + + if(!work) + { + DEBUG(0,("become_domain_master_stage2: Error - cannot find \ +workgroup %s on subnet %s\n", registered_name->name, subrec->subnet_name)); + return; + } + + if((servrec = find_server_in_workgroup( work, lp_netbios_name())) == NULL) + { + DEBUG(0,("become_domain_master_stage2: Error - cannot find server %s \ +in workgroup %s on subnet %s\n", + lp_netbios_name(), registered_name->name, 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 ", lp_netbios_name() ); + 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, lp_netbios_name(), 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, 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) +{ + /* 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, nmbname->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), nmbname->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) +{ + /* 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. */ + become_domain_master_stage1(subrec, question_name->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, nmbname.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, nmbname.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_server_role() == ROLE_DOMAIN_PDC) + { + 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/source4/nmbd/nmbd_become_lmb.c b/source4/nmbd/nmbd_become_lmb.c new file mode 100644 index 0000000000..8b87ca7444 --- /dev/null +++ b/source4/nmbd/nmbd_become_lmb.c @@ -0,0 +1,605 @@ +/* + 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 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 ) +{ + struct name_record *namerec; + + if((namerec = find_name_on_subnet(unicast_subnet, nmbname, FIND_SELF_NAME)) == NULL) + { + /* The name needs to be created on the unicast subnet. */ + (void)add_name_to_subnet( unicast_subnet, nmbname->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, 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, lp_netbios_name())) == NULL) + { + DEBUG(0,("reset_workgroup_state: Error - cannot find server %s \ +in workgroup %s on subnet %s\n", + lp_netbios_name(), 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; + + 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. */ + reset_workgroup_state( subrec, released_name->name, force_new_election ); + + if( DEBUGLVL( 0 ) ) + { + dbgtext( "*****\n\n" ); + dbgtext( "Samba name server %s ", lp_netbios_name() ); + dbgtext( "has stopped being a local master browser " ); + dbgtext( "for workgroup %s ", released_name->name ); + 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; + + 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. */ + reset_workgroup_state( subrec, fail_name->name, force_new_election ); + + if( DEBUGLVL( 0 ) ) + { + dbgtext( "*****\n\n" ); + dbgtext( "Samba name server %s ", lp_netbios_name() ); + dbgtext( "has stopped being a local master browser " ); + dbgtext( "for workgroup %s ", fail_name->name ); + 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, 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; + int 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, lp_netbios_name()) == NULL) + { + DEBUG(0,("unbecome_local_master_browser: Error - cannot find server %s \ +in workgroup %s on subnet %s\n", + lp_netbios_name(), 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 = find_workgroup_on_subnet( subrec, registered_name->name); + struct server_record *servrec; + + if(!work) + { + DEBUG(0,("become_local_master_stage2: Error - cannot find \ +workgroup %s on subnet %s\n", registered_name->name, subrec->subnet_name)); + return; + } + + if((servrec = find_server_in_workgroup( work, lp_netbios_name())) == NULL) + { + DEBUG(0,("become_local_master_stage2: Error - cannot find server %s \ +in workgroup %s on subnet %s\n", + lp_netbios_name(), registered_name->name, 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, lp_netbios_name()); + + /* 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 ", lp_netbios_name() ); + 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) +{ + struct work_record *work = find_workgroup_on_subnet( subrec, fail_name->name); + + 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)); + + if(!work) + { + DEBUG(0,("become_local_master_fail2: Error - cannot find \ +workgroup %s on subnet %s\n", fail_name->name, 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 \ +workgroup %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, lp_netbios_name()) == NULL) + { + DEBUG(0,("become_local_master_fail1: Error - cannot find server %s \ +in workgroup %s on subnet %s\n", + lp_netbios_name(), 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; + int 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, lp_netbios_name()) == NULL) + { + DEBUG(0,("become_local_master_browser: Error - cannot find server %s \ +in workgroup %s on subnet %s\n", + lp_netbios_name(), 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; + fstrcpy(userdata->data, work->work_group); + + /* 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 + + StrnCpy(work->local_master_browser_name, newname, + sizeof(work->local_master_browser_name)-1); +} diff --git a/source4/nmbd/nmbd_browserdb.c b/source4/nmbd/nmbd_browserdb.c new file mode 100644 index 0000000000..a4ef98e265 --- /dev/null +++ b/source4/nmbd/nmbd_browserdb.c @@ -0,0 +1,182 @@ +/* + 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 ) ); + } /* remove_lmb_browser_entry */ + +/* ************************************************************************** ** + * 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 ); + } /* update_browser_death_time */ + +/* ************************************************************************** ** + * 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( char *work_name, + 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 ); + + StrnCpy( browc->lmb_name, browser_name, sizeof(browc->lmb_name)-1 ); + StrnCpy( browc->work_group, work_name, sizeof(browc->work_group)-1 ); + strupper( browc->lmb_name ); + strupper( 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 ); + } /* create_browser_in_lmb_cache */ + +/* ************************************************************************** ** + * 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( 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 ); + } /* find_browser_in_lmb_cache */ + +/* ************************************************************************** ** + * 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 ); + } + } + } /* expire_lmb_browsers */ diff --git a/source4/nmbd/nmbd_browsesync.c b/source4/nmbd/nmbd_browsesync.c new file mode 100644 index 0000000000..ff022a7bb1 --- /dev/null +++ b/source4/nmbd/nmbd_browsesync.c @@ -0,0 +1,699 @@ +/* + 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" + +/* 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; + 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++; + + StrnCpy(p,lp_netbios_name(),15); + strupper(p); + 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), + lp_netbios_name(), 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) +{ + 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 ); + } + + sync_browse_lists(work, work->dmb_name.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--) + { + char qname[17]; + uint16 nb_flags; + int name_type; + + StrnCpy(qname,p,15); + name_type = CVAL(p,15); + nb_flags = get_nb_flags(&p[16]); + trim_string(qname,NULL," "); + + 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->data ); + 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; + int size = sizeof(struct userdata_struct) + sizeof(fstring)+1; + + if( !(work = find_workgroup_on_subnet(subrec, q_name->name)) ) + { + if( DEBUGLVL( 0 ) ) + { + dbgtext( "find_domain_master_name_query_success:\n" ); + dbgtext( "Failed to find workgroup %s\n", q_name->name ); + } + 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. */ + make_nmb_name(&nmbname,"*",0x0); + + /* 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; + fstrcpy(userdata->data, work->work_group); + + 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) +{ + struct nmb_name nmbname; + + /* 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; + } + + make_nmb_name(&nmbname,work->work_group,0x1b); + + /* First, query for the WORKGROUP<1b> name from the WINS server. */ + query_name(unicast_subnet, nmbname.name, nmbname.name_type, + 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; + fstring 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--) + { + char qname[17]; + uint16 nb_flags; + int name_type; + + StrnCpy(qname,p,15); + name_type = CVAL(p,15); + nb_flags = get_nb_flags(&p[16]); + trim_string(qname,NULL," "); + + p += 18; + + if(!(nb_flags & NB_GROUP) && (name_type == 0x00) && + server_name[0] == 0) { + /* this is almost certainly the server netbios name */ + fstrcpy(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 */ + fstrcpy(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; + struct nmb_name nmbname; + + /* 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; + + make_nmb_name(&nmbname,"*",0x1b); + + /* First, query for the *<1b> name from the WINS server. */ + query_name(unicast_subnet, nmbname.name, nmbname.name_type, + 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)) { + 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); + } + + DEBUG(3,("Initiating DMB<->DMB sync with %s(%s)\n", + work->dmb_name.name, + inet_ntoa(work->dmb_addr))); + sync_browse_lists(work, + work->dmb_name.name, + work->dmb_name.name_type, + work->dmb_addr, False, False); + } + } +} diff --git a/source4/nmbd/nmbd_elections.c b/source4/nmbd/nmbd_elections.c new file mode 100644 index 0000000000..759379cc84 --- /dev/null +++ b/source4/nmbd/nmbd_elections.c @@ -0,0 +1,403 @@ +/* + 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" + +/* 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; + 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; + pstrcpy(p,server_name); + strupper(p); + p = skip_string(p,1); + + send_mailslot(False, BROWSE_MAILSLOT, outbuf, PTR_DIFF(p,outbuf), + lp_netbios_name(), 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) +{ + DEBUG(3,("check_for_master_browser_success: Local master browser for workgroup %s exists at \ +IP %s (just checking).\n", answer_name->name, 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) +{ + char *workgroup_name = question_name->name; + struct work_record *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, lp_netbios_name()); + + 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, 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, lp_netbios_name())); + + 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(lp_netbios_name(), 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; + char *server_name = buf+13; + struct work_record *work; + char *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/source4/nmbd/nmbd_incomingdgrams.c b/source4/nmbd/nmbd_incomingdgrams.c new file mode 100644 index 0000000000..3000c347d8 --- /dev/null +++ b/source4/nmbd/nmbd_incomingdgrams.c @@ -0,0 +1,858 @@ +/* + 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; + char *announce_name = buf+5; + uint32 servertype = IVAL(buf,23); + char *comment = buf+31; + struct work_record *work; + struct server_record *servrec; + const char *work_name; + char *source_name = dgram->source_name.name; + + START_PROFILE(host_announce); + comment[43] = 0; + + 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. */ + 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, lp_netbios_name())) + 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); + StrnCpy(servrec->serv.comment,comment,sizeof(servrec->serv.comment)-1); + } + } + 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; + char *workgroup_announce_name = buf+5; + uint32 servertype = IVAL(buf,23); + char *master_name = buf+31; + struct work_record *work; + char *source_name = dgram->source_name.name; + + START_PROFILE(workgroup_announce); + master_name[43] = 0; + + 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(dgram->dest_name.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; + char *server_name = buf+5; + uint32 servertype = IVAL(buf,23); + char *comment = buf+31; + char *work_name; + struct work_record *work; + struct server_record *servrec; + char *source_name = dgram->source_name.name; + + START_PROFILE(local_master_announce); + comment[43] = 0; + + 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. */ + work_name = dgram->dest_name.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); + StrnCpy(servrec->serv.comment,comment,sizeof(servrec->serv.comment)-1); + } + + 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) +{ + char *local_master_name = buf; + struct work_record *work; + struct browse_cache_record *browrec; + + START_PROFILE(master_browser_announce); + local_master_name[15] = 0; + + 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); + char *announce_name = buf+9; + struct work_record *work; + struct server_record *servrec; + const char *work_name; + char *source_name = dgram->source_name.name; + pstring comment; + char *s = buf+9; + + START_PROFILE(lm_host_announce); + s = skip_string(s,1); + StrnCpy(comment, s, 43); + + DEBUG(3,("process_lm_host_announce: LM Announcement 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_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; + } + + /* For a LanMan host announce the workgroup name is the destination name. */ + 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. This code is probably + * not needed in the LanMan announce code, but it won't hurt. + */ + + if(strequal(work_name, lp_netbios_name())) + 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); + StrnCpy(servrec->serv.comment,comment,sizeof(servrec->serv.comment)-1); + } + } + 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; +#if 0 + struct server_record *servrec; +#endif + + 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; + StrnCpy(p,lp_netbios_name(),15); + strupper(p); + 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, lp_netbios_name(),15)) + continue; + + if(!(servrec->serv.type & SV_TYPE_BACKUP_BROWSER)) + continue; + + StrnCpy(p, servrec->serv.name, 15); + strupper(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); + + DEBUG(4,("send_backup_list_response: sending response to %s<00> IP %s with %d servers.\n", + send_to_name->name, inet_ntoa(sendto_ip), count)); + + send_mailslot(True, BROWSE_MAILSLOT, + outbuf,PTR_DIFF(p,outbuf), + lp_netbios_name(), 0, + send_to_name->name,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; + char *workgroup_name = dgram->dest_name.name; + struct subnet_record *search_subrec = subrec; + + START_PROFILE(get_backup_list); + 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; + char *workgroup_name = dgram->dest_name.name; + + START_PROFILE(announce_request); + 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; + char *workgroup_name = dgram->dest_name.name; + + START_PROFILE(lm_announce_request); + 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/source4/nmbd/nmbd_incomingrequests.c b/source4/nmbd/nmbd_incomingrequests.c new file mode 100644 index 0000000000..916f7763f2 --- /dev/null +++ b/source4/nmbd/nmbd_incomingrequests.c @@ -0,0 +1,596 @@ +/* + 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 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; + 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* !!!!! + */ + + if( !group && !ismyip(owner_ip) && strequal(question->name, 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, &nmb->question.question_name, 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) +{ + int l1,l2,l3; + + /* 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(lp_netbios_name()); + + if ((l1==l3) && strncmp(n1,lp_netbios_name(),l3) == 0 && + (l2!=l3 || strncmp(n2,lp_netbios_name(),l3) != 0)) + return -1; + + if ((l2==l3) && strncmp(n2,lp_netbios_name(),l3) == 0 && + (l1!=l3 || strncmp(n1,lp_netbios_name(),l3) != 0)) + return 1; + + return memcmp(n1,n2,18); +} + + +/**************************************************************************** + 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; + char *qname = nmb->question.question_name.name; + 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; + + 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; + + if (!strequal(namerec->name.name,"*") && + !strequal(namerec->name.name,"__SAMBA__") && + (name_type < 0x1b || name_type >= 0x20 || + ques_type < 0x1b || ques_type >= 0x20 || + strequal(qname, namerec->name.name))) + { + /* Start with the name. */ + memset(buf,'\0',18); + slprintf(buf, 17, "%-15.15s",namerec->name.name); + strupper(buf); + + /* 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/source4/nmbd/nmbd_lmhosts.c b/source4/nmbd/nmbd_lmhosts.c new file mode 100644 index 0000000000..c47384c819 --- /dev/null +++ b/source4/nmbd/nmbd_lmhosts.c @@ -0,0 +1,104 @@ +/* + 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) +{ + char *name; + int name_type; + struct in_addr ipaddr; + XFILE *fp = startlmhosts( fname ); + TALLOC_CTX *mem_ctx; + + if (!fp) { + DEBUG(2,("load_lmhosts_file: Can't open lmhosts file %s. Error was %s\n", + fname, strerror(errno))); + return; + } + mem_ctx = talloc_init("load_lmhosts_files"); + if (!mem_ctx) { + DEBUG(2,("load_lmhosts_file: No memory to open lmhosts file %s. Error was %s\n", + fname, strerror(errno))); + return; + } + while (getlmhostsent(mem_ctx, 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/source4/nmbd/nmbd_logonnames.c b/source4/nmbd/nmbd_logonnames.c new file mode 100644 index 0000000000..40edc68800 --- /dev/null +++ b/source4/nmbd/nmbd_logonnames.c @@ -0,0 +1,172 @@ +/* + 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 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) +{ + struct work_record *work = find_workgroup_on_subnet(subrec, fail_name->name); + struct server_record *servrec; + + if(!work) + { + DEBUG(0,("become_logon_server_fail: Error - cannot find \ +workgroup %s on subnet %s\n", fail_name->name, subrec->subnet_name)); + return; + } + + if((servrec = find_server_in_workgroup( work, lp_netbios_name())) == NULL) + { + DEBUG(0,("become_logon_server_fail: Error - cannot find server %s \ +in workgroup %s on subnet %s\n", + lp_netbios_name(), fail_name->name, 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) +{ + struct work_record *work = find_workgroup_on_subnet( subrec, registered_name->name); + struct server_record *servrec; + + if(!work) + { + DEBUG(0,("become_logon_server_success: Error - cannot find \ +workgroup %s on subnet %s\n", registered_name->name, subrec->subnet_name)); + return; + } + + if((servrec = find_server_in_workgroup( work, lp_netbios_name())) == NULL) + { + DEBUG(0,("become_logon_server_success: Error - cannot find server %s \ +in workgroup %s on subnet %s\n", + lp_netbios_name(), registered_name->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/source4/nmbd/nmbd_mynames.c b/source4/nmbd/nmbd_mynames.c new file mode 100644 index 0000000000..dd66821839 --- /dev/null +++ b/source4/nmbd/nmbd_mynames.c @@ -0,0 +1,228 @@ +/* + 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 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) + { + /* The name needs to be created on the unicast subnet. */ + (void)add_name_to_subnet( unicast_subnet, nmbname->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/source4/nmbd/nmbd_namelistdb.c b/source4/nmbd/nmbd_namelistdb.c new file mode 100644 index 0000000000..932d926a91 --- /dev/null +++ b/source4/nmbd/nmbd_namelistdb.c @@ -0,0 +1,624 @@ +/* + 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" + +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. */ + } /* set_samba_nb_type */ + +/* ************************************************************************** ** + * Convert a NetBIOS name to upper case. + * ************************************************************************** ** + */ +static void upcase_name( struct nmb_name *target, struct nmb_name *source ) + { + int i; + + if( NULL != source ) + (void)memcpy( target, source, sizeof( struct nmb_name ) ); + + strupper( target->name ); + strupper( target->scope ); + + /* 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'; + } /* upcase_name */ + +/* ************************************************************************** ** + * 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; + + (void)ubi_trInsert( subrec->namelist, namerec, &(namerec->name), &oldrec ); + if( oldrec ) + { + SAFE_FREE( oldrec->data.ip ); + SAFE_FREE( oldrec ); + } + } /* update_name_in_namelist */ + +/* ************************************************************************** ** + * Remove a name from the namelist. + * ************************************************************************** ** + */ +void remove_name_from_namelist( struct subnet_record *subrec, + struct name_record *namerec ) + { + (void)ubi_trRemove( subrec->namelist, namerec ); + + SAFE_FREE(namerec->data.ip); + + ZERO_STRUCTP(namerec); + SAFE_FREE(namerec); + + subrec->namelist_changed = True; + } /* remove_name_from_namelist */ + +/* ************************************************************************** ** + * 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_name_on_subnet */ + +/* ************************************************************************** ** + * 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 ); + } /* find_name_for_remote_broadcast_subnet */ + +/* ************************************************************************** ** + * 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; +} /* update_name_ttl */ + +/* ************************************************************************** ** + * 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 ) + (void)add_name_to_subnet( subrec, nmbname->name, nmbname->name_type, + nb_flags, ttl, SELF_NAME, 1, ®istered_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; + + } + + (void)add_name_to_subnet(subrec,"*",0x0,samba_nb_type, PERMANENT_TTL, + PERMANENT_NAME, num_ips, iplist); + (void)add_name_to_subnet(subrec,"*",0x20,samba_nb_type,PERMANENT_TTL, + PERMANENT_NAME, num_ips, iplist); + (void)add_name_to_subnet(subrec,"__SAMBA__",0x20,samba_nb_type,PERMANENT_TTL, + PERMANENT_NAME, num_ips, iplist); + (void)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/source4/nmbd/nmbd_namequery.c b/source4/nmbd/nmbd_namequery.c new file mode 100644 index 0000000000..8995e9ac52 --- /dev/null +++ b/source4/nmbd/nmbd_namequery.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. + +*/ + +#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, 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, + 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/source4/nmbd/nmbd_nameregister.c b/source4/nmbd/nmbd_nameregister.c new file mode 100644 index 0000000000..7bf2584053 --- /dev/null +++ b/source4/nmbd/nmbd_nameregister.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-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" + +/* 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(®ister_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 */ + if((nmb->header.rcode == ACT_ERR) && strequal(lp_workgroup(), answer_name->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(®ister_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(®istered_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; + + 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; + } + + add_name_to_subnet(unicast_subnet, nmbname->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; + + 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/source4/nmbd/nmbd_namerelease.c b/source4/nmbd/nmbd_namerelease.c new file mode 100644 index 0000000000..0611ca9323 --- /dev/null +++ b/source4/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/source4/nmbd/nmbd_nodestatus.c b/source4/nmbd/nmbd_nodestatus.c new file mode 100644 index 0000000000..993e4d9d17 --- /dev/null +++ b/source4/nmbd/nmbd_nodestatus.c @@ -0,0 +1,94 @@ +/* + 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 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/source4/nmbd/nmbd_packets.c b/source4/nmbd/nmbd_packets.c new file mode 100644 index 0000000000..6ee13812dc --- /dev/null +++ b/source4/nmbd/nmbd_packets.c @@ -0,0 +1,2013 @@ +/* + 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 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, ®ister_ip); + else + ret = initiate_multihomed_name_register_packet(p, nb_flags, ®ister_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); + + /* Drop the packet if it's a different NetBIOS scope, or + the source is from one of our names. */ + + if (!strequal(dgram->dest_name.scope, lp_netbios_scope())) + { + DEBUG(7,("process_browse_packet: Discarding datagram from IP %s. Scope (%s) \ +mismatch with our scope (%s).\n", inet_ntoa(p->ip), dgram->dest_name.scope, lp_netbios_scope())); + return; + } + + if (is_myname(dgram->source_name.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))); + } + } +} + +/**************************************************************************** + 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); + + /* Drop the packet if it's a different NetBIOS scope, or + the source is from one of our names. */ + + if (!strequal(dgram->dest_name.scope, lp_netbios_scope())) + { + DEBUG(7,("process_lanman_packet: Discarding datagram from IP %s. Scope (%s) \ +mismatch with our scope (%s).\n", inet_ntoa(p->ip), dgram->dest_name.scope, lp_netbios_scope())); + return; + } + + if (is_myname(dgram->source_name.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))); + } + } +} + +/**************************************************************************** + 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 ). +******************************************************************/ + +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,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) +{ + 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,23 + 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); + pstrcpy(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 = 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/source4/nmbd/nmbd_processlogon.c b/source4/nmbd/nmbd_processlogon.c new file mode 100644 index 0000000000..1fcfd11a3e --- /dev/null +++ b/source4/nmbd/nmbd_processlogon.c @@ -0,0 +1,480 @@ +/* + 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) Jim McDonough 2002 + Copyright (C) Anthony Liguori 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(3,("process_logon_packet: Logon packet received from IP %s and domain \ +logons are not enabled.\n", inet_ntoa(p->ip) )); + return; + } + + pstrcpy(my_name, lp_netbios_name()); + + code = SVAL(buf,0); + DEBUG(1,("process_logon_packet: Logon from %s: code = 0x%x\n", inet_ntoa(p->ip), code)); + + switch (code) + { + case 0: + { + 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); + + DEBUG(3,("process_logon_packet: Domain login request from %s at IP %s user=%s token=%x\n", + machine,inet_ntoa(p->ip),user,token)); + + q = outbuf; + SSVAL(q, 0, 6); + q += 2; + + fstrcpy(reply_name, "\\\\"); + fstrcat(reply_name, my_name); + fstrcpy(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, + outbuf,PTR_DIFF(q,outbuf), + lp_netbios_name(), 0x0, + machine, + dgram->source_name.name_type, + p->ip, *iface_ip(p->ip), p->port); + break; + } + + case QUERYFORPDC: + { + 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); + fstrcpy(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 */ + + DEBUG(3,("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", + machine,inet_ntoa(p->ip), reply_name, lp_workgroup(), + QUERYFORPDC_R, (uint32)ntversion, (uint32)lmnttoken, + (uint32)lm20token )); + + dump_data(4, outbuf, PTR_DIFF(q, outbuf)); + + send_mailslot(True, getdc, + outbuf,PTR_DIFF(q,outbuf), + lp_netbios_name(), 0x0, + dgram->source_name.name, + dgram->source_name.name_type, + p->ip, *iface_ip(p->ip), p->port); + return; + } + + case SAMLOGON: + { + 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(3,("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(3,("process_logon_packet: len = %d PTR_DIFF(q, buf) = %d\n", len, 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(3,("process_logon_packet: SAMLOGON user %s\n", ascuser)); + + fstrcpy(reply_name, "\\\\"); /* Here it wants \\LOGONSERVER. */ + fstrcat(reply_name, my_name); + + DEBUG(3,("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 { + GUID domain_guid; + pstring domain; + char *hostname = NULL; + char *component, *dc, *q1; + uint8 size; + + get_mydomname(domain); + hostname = get_myname(); + + if (SVAL(uniuser, 0) == 0) { + SSVAL(q, 0, SAMLOGON_AD_UNK_R); /* user unknown */ + } else { + SSVAL(q, 0, SAMLOGON_AD_R); + } + q += 2; + + SSVAL(q, 0, 0); + q += 2; + 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; + } + memcpy(q, &domain_guid, sizeof(domain_guid)); + q += sizeof(domain_guid); + + /* Push domain components */ + 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); + } + SCVAL(q, 0, 0); q++; + SSVAL(q, 0, 0x18c0); /* not sure what this is for, but */ + q += 2; /* it must follow the domain name. */ + + /* Push dns host name */ + size = push_ascii(&q[1], hostname, -1, 0); + SCVAL(q, 0, size); + q += (size + 1); + SSVAL(q, 0, 0x18c0); /* not sure what this is for, but */ + q += 2; /* it must follow the domain name. */ + + /* Push NETBIOS of domain */ + size = push_ascii(&q[1], lp_workgroup(), -1, STR_UPPER); + SCVAL(q, 0, size); + q += (size + 1); + SCVAL(q, 0, 0); q++; /* is this a null terminator or empty field */ + /* null terminator would not be needed because size is included */ + + /* Push NETBIOS of hostname */ + size = push_ascii(&q[1], my_name, -1, 0); + SCVAL(q, 0, size); + q += (size + 1); + SCVAL(q, 0, 0); q++; /* null terminator or empty field? */ + + /* Push user account */ + size = push_ascii(&q[1], ascuser, -1, 0); + SCVAL(q, 0, size); + q += (size + 1); + + /* Push 'Default-First-Site-Name' */ + size = push_ascii(&q[1], "Default-First-Site-Name", -1, 0); + SCVAL(q, 0, size); + q += (size + 1); + + SSVAL(q, 0, 0xc000); /* unknown */ + SCVAL(q, 2, PTR_DIFF(q,q1)); + SCVAL(q, 3, 0x10); /* unknown */ + q += 4; + + 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 */ + if (hostname) free(hostname); + } +#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)); + + send_mailslot(True, getdc, + outbuf,PTR_DIFF(q,outbuf), + lp_netbios_name(), 0x0, + dgram->source_name.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 */ + + 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) */ + + 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/source4/nmbd/nmbd_responserecordsdb.c b/source4/nmbd/nmbd_responserecordsdb.c new file mode 100644 index 0000000000..7e8c8025ae --- /dev/null +++ b/source4/nmbd/nmbd_responserecordsdb.c @@ -0,0 +1,264 @@ +/* + 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/source4/nmbd/nmbd_sendannounce.c b/source4/nmbd/nmbd_sendannounce.c new file mode 100644 index 0000000000..191a3b7c7b --- /dev/null +++ b/source4/nmbd/nmbd_sendannounce.c @@ -0,0 +1,607 @@ +/* + 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), + lp_netbios_name(), 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, lp_netbios_name(), 15, STR_ASCII|STR_UPPER|STR_TERMINATE); + + send_mailslot(False, BROWSE_MAILSLOT, outbuf,PTR_DIFF(p,outbuf), + lp_netbios_name(), 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; + 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. */ + + push_string(NULL, p+5, server_name, 15, STR_ASCII|STR_UPPER|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; + /*StrnCpy(p,server_name,15); + strupper(p); + p = skip_string(p,1); + pstrcpy(p,server_comment); + p = skip_string(p,1);*/ + 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, lp_netbios_name(), subrec->subnet_name, work->work_group)); + + send_announcement(subrec, ANN_LocalMasterAnnouncement, + lp_netbios_name(), /* From nbt name. */ + work->work_group, 0x1e, /* To nbt name. */ + subrec->bcast_ip, /* To ip. */ + work->announce_interval, /* Time until next announce. */ + lp_netbios_name(), /* 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, + lp_netbios_name(), /* 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. */ + lp_netbios_name()); /* 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. */ + 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(lp_netbios_name(),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; + + 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++; + + StrnCpy(p,lp_netbios_name(),15); + strupper(p); + 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", + lp_netbios_name(), inet_ntoa(addr) )); + + send_mailslot(True, BROWSE_MAILSLOT, outbuf,PTR_DIFF(p,outbuf), + lp_netbios_name(), 0x0, "*", 0x0, addr, FIRST_SUBNET->myip, DGRAM_PORT); + } +} diff --git a/source4/nmbd/nmbd_serverlistdb.c b/source4/nmbd/nmbd_serverlistdb.c new file mode 100644 index 0000000000..ee0c021d5d --- /dev/null +++ b/source4/nmbd/nmbd_serverlistdb.c @@ -0,0 +1,448 @@ +/* + 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; + + StrnCpy(servrec->serv.name,name,sizeof(servrec->serv.name)-1); + StrnCpy(servrec->serv.comment,comment,sizeof(servrec->serv.comment)-1); + strupper(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_string(fname,NULL,"/"); + 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/source4/nmbd/nmbd_subnetdb.c b/source4/nmbd/nmbd_subnetdb.c new file mode 100644 index 0000000000..6296826425 --- /dev/null +++ b/source4/nmbd/nmbd_subnetdb.c @@ -0,0 +1,361 @@ +/* + 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) ) ); + } /* namelist_entry_compare */ + + +/**************************************************************************** +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 + 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/source4/nmbd/nmbd_synclists.c b/source4/nmbd/nmbd_synclists.c new file mode 100644 index 0000000000..b9952fb446 --- /dev/null +++ b/source4/nmbd/nmbd_synclists.c @@ -0,0 +1,300 @@ +/* + 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; + fstring workgroup; + fstring 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); + + fstrcpy(s->workgroup, work->work_group); + fstrcpy(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 */ + fstrcpy(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; + fstring 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/source4/nmbd/nmbd_winsproxy.c b/source4/nmbd/nmbd_winsproxy.c new file mode 100644 index 0000000000..2e65ebb612 --- /dev/null +++ b/source4/nmbd/nmbd_winsproxy.c @@ -0,0 +1,221 @@ +/* + 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) +{ + 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(); + + namerec = add_name_to_subnet( orig_broadcast_subnet, nmbname->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 *)©_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; + + 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. */ + query_name( unicast_subnet, question_name->name, question_name->name_type, + wins_proxy_name_query_request_success, + wins_proxy_name_query_request_fail, + userdata); +} diff --git a/source4/nmbd/nmbd_winsserver.c b/source4/nmbd/nmbd_winsserver.c new file mode 100644 index 0000000000..4ef476f814 --- /dev/null +++ b/source4/nmbd/nmbd_winsserver.c @@ -0,0 +1,2032 @@ +/* + 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" + +#define WINS_LIST "wins.tdb" +#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; + 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; + } + } + + p = command; + p += slprintf(p, sizeof(command)-1, "%s %s %s %02x %d", + cmd, + operation, + namerec->name.name, + namerec->name.name_type, + ttl); + + for (i=0;idata.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); + TDB_CONTEXT *tdb; + TDB_DATA kbuf, dbuf, newkey; + struct name_record *namerec = NULL; + struct in_addr our_fake_ip = *interpret_addr2("0.0.0.0"); + + DEBUG(2,("initialise_wins: started\n")); + + if(!lp_we_are_a_wins_server()) + return True; + + add_samba_names_to_subnet(wins_server_subnet); + + tdb = tdb_open_log(lock_path(WINS_LIST), 0, TDB_DEFAULT, O_RDONLY, 0600); + if (!tdb) { + DEBUG(2,("initialise_wins: Can't open wins database file %s. Error was %s\n", WINS_LIST, strerror(errno) )); + return True; + } + + if (tdb_fetch_int32(tdb, INFO_VERSION) != WINS_VERSION) { + DEBUG(0,("Discarding invalid wins.dat file\n")); + tdb_close(tdb); + return True; + } + + 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 high, low; + struct in_addr wins_ip; + struct in_addr *ip_list; + int wins_flags; + int len,i; + + 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); + + /* Don't reload replica records */ + if (!ip_equal(wins_ip, our_fake_ip)) { + SAFE_FREE(dbuf.dptr); + continue; + } + + /* Don't reload released or tombstoned records */ + if ((wins_flags&WINS_STATE_MASK) != WINS_ACTIVE) { + SAFE_FREE(dbuf.dptr); + continue; + } + + /* Allocate the space for the ip_list. */ + if((ip_list = (struct in_addr *)malloc( num_ips * sizeof(struct in_addr))) == NULL) { + SAFE_FREE(dbuf.dptr); + DEBUG(0,("initialise_wins: Malloc fail !\n")); + return False; + } + + 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); + } + + /* 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)); + + namerec=add_name_to_subnet( wins_server_subnet, name, type, nb_flags, + ttl, REGISTER_NAME, num_ips, ip_list); + if (namerec!=NULL) { + update_wins_owner(namerec, wins_ip); + update_wins_flag(namerec, wins_flags); + /* we don't reload the ID, on startup we restart at 1 */ + get_global_id_and_update(&namerec->data.id, True); + } + + } 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(dbuf.dptr); + SAFE_FREE(ip_list); + } + + tdb_close(tdb); + DEBUG(2,("initialise_wins: done\n")); + 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. + */ + + DEBUG(0,("wins_process_name_refresh_request: broadcast name refresh 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_refresh_request: Name refresh for name %s \ +IP %s\n", nmb_namestr(question), inet_ntoa(from_ip) )); + + /* + * See if the name already exists. + */ + + 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) + { + DEBUG(3,("wins_process_name_refresh_request: Name refresh for name %s and \ +the name does not exist. Treating as registration.\n", nmb_namestr(question) )); + wins_process_name_registration_request(subrec,p); + return; + } + + /* + * if the name is present but not active, + * simply remove it and treat the request + * as a registration + */ + if (namerec != NULL && !WINS_STATE_ACTIVE(namerec)) + { + DEBUG(5,("wins_process_name_refresh_request: Name (%s) in WINS was \ +not active - removing it.\n", nmb_namestr(question) )); + 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((namerec != NULL) && ((group && !NAME_GROUP(namerec)) || (!group && NAME_GROUP(namerec))) ) + { + DEBUG(3,("wins_process_name_refresh_request: Name %s group bit = %s \ +does not match group bit in WINS for this name.\n", nmb_namestr(question), group ? "True" : "False" )); + 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) + { + /* + * 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. + */ + + DEBUG(3,("wins_process_name_refresh_request: Name refresh for name %s with IP %s and \ +is IP is not known to the name.\n", nmb_namestr(question), inet_ntoa(from_ip) )); + 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) +{ + 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) && (is_myname(namerec->name.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. + */ + + query_name_from_wins_server( *namerec->data.ip, + question->name, + question->name_type, + wins_register_query_success, + wins_register_query_fail, + userdata ); + return; + } + + /* + * Name did not exist - add it. + */ + + (void)add_name_to_subnet( subrec, question->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"); + + 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 + */ + + query_name_from_wins_server( namerec->data.ip[0], + question->name, + question->name_type, + wins_multihomed_register_query_success, + wins_multihomed_register_query_fail, + userdata ); + + return; + } + + /* + * Name did not exist - add it. + */ + + (void)add_name_to_subnet( subrec, question->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 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; + + 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. + */ + + if(strequal( question->name, "*") && (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 anymore active.\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; + TDB_CONTEXT *tdb; + TDB_DATA kbuf, dbuf; + pstring key, buf; + int len; + int num_record=0; + SMB_BIG_UINT id; + + 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()); + + tdb = tdb_open_log(fnamenew, 0, TDB_DEFAULT, O_RDWR|O_CREAT|O_TRUNC, 0644); + if (!tdb) { + DEBUG(0,("wins_write_database: Can't open %s. Error was %s\n", fnamenew, strerror(errno))); + if (background) + _exit(0); + return; + } + + DEBUG(3,("wins_write_database: Dump of WINS name list.\n")); + + tdb_store_int32(tdb, INFO_VERSION, WINS_VERSION); + + 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(3,("%-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_m( ts, '\n' ); + if( NULL != nl ) + *nl = '\0'; + + DEBUGADD(3,("TTL = %s ", ts )); + } else + DEBUGADD(3,("TTL = PERMANENT ")); + + for (i = 0; i < namerec->data.num_ips; i++) + DEBUGADD(0,("%15s ", inet_ntoa(namerec->data.ip[i]) )); + + DEBUGADD(3,("0x%2x 0x%2x %15s\n", namerec->data.nb_flags, namerec->data.wins_flags, inet_ntoa(namerec->data.wins_ip))); + + if( namerec->data.source == REGISTER_NAME ) { + + /* store the type in the key to make the name unique */ + slprintf(key, sizeof(key), "%s%s#%02x", ENTRY_PREFIX, namerec->name.name, namerec->name.name_type); + + len = tdb_pack(buf, sizeof(buf), "dddfddd", + (int)namerec->data.nb_flags, + (int)(namerec->data.id>>32), + (int)(namerec->data.id&0xffffffff), + inet_ntoa(namerec->data.wins_ip), + (int)namerec->data.death_time, + namerec->data.num_ips, + namerec->data.wins_flags); + + for (i = 0; i < namerec->data.num_ips; i++) + len += tdb_pack(buf+len, sizeof(buf)-len, "f", inet_ntoa(namerec->data.ip[i])); + + kbuf.dsize = strlen(key)+1; + kbuf.dptr = key; + dbuf.dsize = len; + dbuf.dptr = buf; + if (tdb_store(tdb, kbuf, dbuf, TDB_INSERT) != 0) return; + + num_record++; + } + } + + /* store the number of records */ + tdb_store_int32(tdb, INFO_COUNT, num_record); + + /* get and store the last used ID */ + get_global_id_and_update(&id, False); + tdb_store_int32(tdb, INFO_ID_HIGH, id>>32); + tdb_store_int32(tdb, INFO_ID_LOW, id&0xffffffff); + + tdb_close(tdb); + + 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=(WINS_RECORD *)buf; + + ZERO_STRUCT(question); + memcpy(question.name, record->name, 16); + question.name_type=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; inum_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/source4/nmbd/nmbd_workgroupdb.c b/source4/nmbd/nmbd_workgroupdb.c new file mode 100644 index 0000000000..3e177bceb4 --- /dev/null +++ b/source4/nmbd/nmbd_workgroupdb.c @@ -0,0 +1,342 @@ +/* + 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; + 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)); + + StrnCpy(work->work_group,name,sizeof(work->work_group)-1); + 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 (!strcmp(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(lp_netbios_name(), 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/source4/nsswitch/.cvsignore b/source4/nsswitch/.cvsignore new file mode 100644 index 0000000000..658d50a680 --- /dev/null +++ b/source4/nsswitch/.cvsignore @@ -0,0 +1,4 @@ +*.po +*.po32 +diffs +winbindd_proto.h diff --git a/source4/nsswitch/README b/source4/nsswitch/README new file mode 100644 index 0000000000..9f0c581df6 --- /dev/null +++ b/source4/nsswitch/README @@ -0,0 +1,13 @@ +This extension provides a "wins" module for NSS on glibc2/Linux. This +allows you to use a WINS entry in /etc/nsswitch.conf for hostname +resolution, allowing you to resolve netbios names via start unix +gethostbyname() calls. The end result is that you can use netbios +names as host names in unix apps. + +1) run configure +2) run "make nsswitch" +3) cp nsswitch/libnss_wins.so /lib/libnss_wins.so.2 +4) add a wins entry to the hosts line in /etc/nsswitch.conf +5) use it + +tridge@linuxcare.com diff --git a/source4/nsswitch/hp_nss_common.h b/source4/nsswitch/hp_nss_common.h new file mode 100644 index 0000000000..5bd5374182 --- /dev/null +++ b/source4/nsswitch/hp_nss_common.h @@ -0,0 +1,51 @@ +#ifndef _HP_NSS_COMMON_H +#define _HP_NSS_COMMON_H + +/* + 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. +*/ + +#ifdef HAVE_SYNCH_H +#include +#endif +#ifdef HAVE_PTHREAD_H +#include +#endif + +typedef enum { + NSS_SUCCESS, + NSS_NOTFOUND, + NSS_UNAVAIL, + NSS_TRYAGAIN +} nss_status_t; + +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; + +#endif /* _HP_NSS_COMMON_H */ diff --git a/source4/nsswitch/hp_nss_dbdefs.h b/source4/nsswitch/hp_nss_dbdefs.h new file mode 100644 index 0000000000..bd24772e33 --- /dev/null +++ b/source4/nsswitch/hp_nss_dbdefs.h @@ -0,0 +1,105 @@ +#ifndef _HP_NSS_DBDEFS_H +#define _HP_NSS_DBDEFS_H + +/* + 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. +*/ + +#include +#include +#include + +#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 /* _NSS_DBDEFS_H */ diff --git a/source4/nsswitch/nss.h b/source4/nsswitch/nss.h new file mode 100644 index 0000000000..d83a5e237e --- /dev/null +++ b/source4/nsswitch/nss.h @@ -0,0 +1,104 @@ +#ifndef _NSSWITCH_NSS_H +#define _NSSWITCH_NSS_H +/* + 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. +*/ + +#ifdef HAVE_NSS_COMMON_H + +/* Sun Solaris */ + +#include +#include +#include + +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 + +#elif HAVE_NSS_H + +/* GNU */ + +#include + +typedef enum nss_status NSS_STATUS; + +#elif HAVE_NS_API_H + +/* SGI IRIX */ + +/* following required to prevent warnings of double definition + * of datum from ns_api.h +*/ +#ifdef DATUM +#define _DATUM_DEFINED +#endif + +#include + +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 + +#elif defined(HPUX) && defined(HAVE_NSSWITCH_H) +/* HP-UX 11 */ + +#include "nsswitch/hp_nss_common.h" +#include "nsswitch/hp_nss_dbdefs.h" +#include + +#ifndef _HAVE_TYPEDEF_NSS_STATUS +#define _HAVE_TYPEDEF_NSS_STATUS +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 +#endif /* HPUX */ + +#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/source4/nsswitch/pam_winbind.c b/source4/nsswitch/pam_winbind.c new file mode 100644 index 0000000000..123f670366 --- /dev/null +++ b/source4/nsswitch/pam_winbind.c @@ -0,0 +1,754 @@ +/* pam_winbind module + + Copyright Andrew Tridgell 2000 + Copyright Tim Potter 2000 + Copyright Andrew Bartlett 2002 + + largely based on pam_userdb by Christian Gafton + also contains large slabs of code from pam_unix by Elliot Lee + (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); + + /* */ + const char *user; + char *pass_old, *pass_new; + /* */ + + 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 2000 + * Copyright (c) Tim Potter 2000 + * Copyright (c) Andrew Bartlettt 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 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/source4/nsswitch/pam_winbind.h b/source4/nsswitch/pam_winbind.h new file mode 100644 index 0000000000..fae635d806 --- /dev/null +++ b/source4/nsswitch/pam_winbind.h @@ -0,0 +1,93 @@ +/* pam_winbind header file + (Solaris needs some macros from Linux for common PAM code) + + Shirish Kalele 2000 +*/ + +#ifdef HAVE_FEATURES_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define MODULE_NAME "pam_winbind" +#define PAM_SM_AUTH +#define PAM_SM_ACCOUNT +#define PAM_SM_PASSWORD + +#if defined(SUNOS5) || defined(SUNOS4) || defined(HPUX) + +/* Solaris always uses dynamic pam modules */ +#define PAM_EXTERN extern +#include + +#define PAM_AUTHTOK_RECOVER_ERR PAM_AUTHTOK_RECOVERY_ERR +#endif + +#ifdef HAVE_SECURITY_PAM_MODULES_H +#include +#endif + +#ifdef HAVE_SECURITY__PAM_MACROS_H +#include +#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 %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); +} + +/* 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; +} + +/* 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, int size, gid_t *list) +{ + gid_t *groups = NULL; + int result, i; + + /* + * 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 */ + + result = wb_getgroups(user, &groups); + + if (size == 0) + goto done; + + if (result > size) { + result = -1; + errno = EINVAL; /* This is what getgroups() does */ + goto done; + } + + /* Copy list of groups across */ + + for (i = 0; i < result; i++) { + list[i] = groups[i]; + } + + done: + SAFE_FREE(groups); + return result; +} diff --git a/source4/nsswitch/wb_common.c b/source4/nsswitch/wb_common.c new file mode 100644 index 0000000000..89c751a4ef --- /dev/null +++ b/source4/nsswitch/wb_common.c @@ -0,0 +1,433 @@ +/* + 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; + } + /* Parinoia */ + 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 */ + +int winbind_open_pipe_sock(void) +{ +#ifdef HAVE_UNIXSOCKET + struct sockaddr_un sunaddr; + static pid_t our_pid; + struct stat st; + pstring path; + int fd; + + if (our_pid != getpid()) { + close_sock(); + our_pid = getpid(); + } + + if (winbindd_fd != -1) { + return winbindd_fd; + } + + /* Check permissions on unix socket directory */ + + if (lstat(WINBINDD_SOCKET_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, WINBINDD_SOCKET_DIR, sizeof(path) - 1); + path[sizeof(path) - 1] = '\0'; + + strncat(path, "/", sizeof(path) - 1); + path[sizeof(path) - 1] = '\0'; + + strncat(path, WINBINDD_SOCKET_NAME, sizeof(path) - 1); + 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 ((winbindd_fd = make_safe_fd( fd)) == -1) { + return winbindd_fd; + } + + if (connect(winbindd_fd, (struct sockaddr *)&sunaddr, + sizeof(sunaddr)) == -1) { + close_sock(); + return -1; + } + + 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; + + /* Check for our tricky environment variable */ + + if (getenv(WINBINDD_DONT_ENV)) { + 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); +} diff --git a/source4/nsswitch/wbinfo.c b/source4/nsswitch/wbinfo.c new file mode 100644 index 0000000000..68dc178bcd --- /dev/null +++ b/source4/nsswitch/wbinfo.c @@ -0,0 +1,891 @@ +/* + Unix SMB/CIFS implementation. + + Winbind status program. + + Copyright (C) Tim Potter 2000-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" +#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(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; +} + +/* 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(void) +{ + struct winbindd_response response; + + ZERO_STRUCT(response); + + /* Send request */ + + if (winbindd_request(WINBINDD_SHOW_SEQUENCE, NULL, &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; +} + +/* 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; +} + +/* 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 %d\n", response.data.sid.sid, 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)\n", + response.data.auth.nt_status_string, + response.data.auth.nt_status); + + 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); + + fstrcpy(request.data.auth_crap.user, name_user); + + fstrcpy(request.data.auth_crap.domain, name_domain); + + 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)\n", + response.data.auth.nt_status_string, + response.data.auth.nt_status); + + return result == NSS_STATUS_SUCCESS; +} + +/* Print domain users */ + +static BOOL print_domain_users(void) +{ + struct winbindd_response response; + const char *extra_data; + fstring name; + + /* Send request to winbind daemon */ + + ZERO_STRUCT(response); + + if (winbindd_request(WINBINDD_LIST_USERS, NULL, &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(void) +{ + struct winbindd_response response; + const char *extra_data; + fstring name; + + ZERO_STRUCT(response); + + if (winbindd_request(WINBINDD_LIST_GROUPS, NULL, &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 + 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_init(); + + user = secrets_fetch(SECRETS_AUTH_USER, NULL); + domain = secrets_fetch(SECRETS_AUTH_DOMAIN, NULL); + password = secrets_fetch(SECRETS_AUTH_PASSWORD, NULL); + + if (!user && !domain && !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 ? "\\" : "", + 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_SEQUENCE +}; + +int main(int argc, char **argv) +{ + int opt; + + poptContext pc; + static char *string_arg; + static int int_arg; + BOOL got_command = False; + 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-groups", 'g', POPT_ARG_NONE, 0, 'g', "Lists all domain groups" }, + { "WINS-by-name", 'N', POPT_ARG_STRING, &string_arg, 'N', "Converts NetBIOS name to IP (WINS)", "NETBIOS-NAME" }, + { "WINS-by-ip", 'I', POPT_ARG_STRING, &string_arg, 'I', "Converts IP address to NetBIOS name (WINS)", "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" }, + { "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" }, + { "user-groups", 'r', POPT_ARG_STRING, &string_arg, 'r', "Get user groups", "USER" }, + { "authenticate", 'a', POPT_ARG_STRING, &string_arg, 'a', "authenticate user", "user%password" }, + { "set-auth-user", 'A', 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" }, + { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_version}, + { 0, 0, 0, 0 } + }; + + /* 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) { + if (got_command) { + d_fprintf(stderr, "No more than one command may be specified at once.\n"); + exit(1); + } + got_command = True; + } + + 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()) { + d_printf("Error looking up domain users\n"); + goto done; + } + break; + case 'g': + if (!print_domain_groups()) { + 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 '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()) { + d_printf("Could not show sequence numbers\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 '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 '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; + 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/source4/nsswitch/winbind_client.h b/source4/nsswitch/winbind_client.h new file mode 100644 index 0000000000..4de2d57cc7 --- /dev/null +++ b/source4/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/source4/nsswitch/winbind_nss.c b/source4/nsswitch/winbind_nss.c new file mode 100644 index 0000000000..0b4c0ce1d0 --- /dev/null +++ b/source4/nsswitch/winbind_nss.c @@ -0,0 +1,1341 @@ +/* + 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 +#endif + +#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)); +} + +#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_ntdom_* + functions. */ + +static char *get_static(char **buffer, int *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; +} + +/* 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; +} + +#endif diff --git a/source4/nsswitch/winbind_nss_config.h b/source4/nsswitch/winbind_nss_config.h new file mode 100644 index 0000000000..2faaa30d1b --- /dev/null +++ b/source4/nsswitch/winbind_nss_config.h @@ -0,0 +1,165 @@ +/* + 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 +#endif + +#include + +#ifdef HAVE_STDLIB_H +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_SYS_SELECT_H +#include +#endif + +#ifdef HAVE_SYS_SOCKET_H +#include +#endif + +#ifdef HAVE_UNIXSOCKET +#include +#endif + +#ifdef HAVE_SYS_TIME_H +#include +#endif + +#ifdef HAVE_GRP_H +#include +#endif + +#ifdef HAVE_STRING_H +#include +#endif + +#ifdef HAVE_FCNTL_H +#include +#else +#ifdef HAVE_SYS_FCNTL_H +#include +#endif +#endif + +#include +#include +#include +#include +#include "nsswitch/nss.h" + +/* Declarations for functions in winbind_nss.c + needed in winbind_nss_solaris.c (solaris wrapper to nss) */ + +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); + +/* 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/source4/nsswitch/winbind_nss_solaris.c b/source4/nsswitch/winbind_nss_solaris.c new file mode 100644 index 0000000000..f3bd05b77a --- /dev/null +++ b/source4/nsswitch/winbind_nss_solaris.c @@ -0,0 +1,301 @@ +/* + 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 +#include +#include +#include +#include +#include "includes.h" +#include +#if !defined(HPUX) +#include +#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) +{ + NSS_DEBUG("_nss_winbind_getgroupsbymember"); + 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/source4/nsswitch/winbindd.c b/source4/nsswitch/winbindd.c new file mode 100644 index 0000000000..ad37768c09 --- /dev/null +++ b/source4/nsswitch/winbindd.c @@ -0,0 +1,951 @@ +/* + Unix SMB/CIFS implementation. + + Winbind daemon for ntdom nss module + + Copyright (C) by Tim Potter 2000-2002 + 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 "winbindd.h" + +BOOL opt_nocache = False; +BOOL opt_dual_daemon = False; + +/* Reload configuration */ + +static BOOL reload_services_file(BOOL test) +{ + BOOL ret; + pstring logfile; + + if (lp_loaded()) { + pstring fname; + + pstrcpy(fname,lp_configfile()); + if (file_exist(fname,NULL) && !strcsequal(fname,dyn_CONFIGFILE)) { + pstrcpy(dyn_CONFIGFILE,fname); + test = False; + } + } + + snprintf(logfile, sizeof(logfile), "%s/log.winbindd", dyn_LOGFILEBASE); + lp_set_logfile(logfile); + + reopen_logs(); + ret = lp_load(dyn_CONFIGFILE,False,False,True); + + snprintf(logfile, sizeof(logfile), "%s/log.winbindd", dyn_LOGFILEBASE); + lp_set_logfile(logfile); + + reopen_logs(); + load_interfaces(); + + return(ret); +} + +/******************************************************************* + Print out all talloc memory info. +********************************************************************/ + +void return_all_talloc_info(int msg_type, pid_t src_pid, void *buf, size_t len) +{ + TALLOC_CTX *ctx = talloc_init("info context"); + char *info = NULL; + + if (!ctx) + return; + + info = talloc_describe_all(ctx); + if (info) + DEBUG(10,(info)); + message_send_pid(src_pid, MSG_TALLOC_USAGE, info, info ? strlen(info) + 1: 0, True); + talloc_destroy(ctx); +} + +#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 %d, sock %d, rbl %d, wbl %d\n", + 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_idmap_status(); + winbindd_cm_status(); +} + +/* Flush client cache */ + +static void flush_caches(void) +{ + /* Clear cached user and group enumation info */ + wcache_flush_cache(); +} + +/* Handle the signal by unlinking socket and exiting */ + +static void terminate(void) +{ + pstring path; + + winbindd_idmap_close(); + + /* Remove socket file */ + snprintf(path, sizeof(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(); +} + +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" }, + + /* 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" }, + + /* 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_NETBIOS_NAME, winbindd_netbios_name, "NETBIOS_NAME" }, + + /* WINS functions */ + + { WINBINDD_WINS_BYNAME, winbindd_wins_byname, "WINS_BYNAME" }, + { WINBINDD_WINS_BYIP, winbindd_wins_byip, "WINS_BYIP" }, + + /* 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) +{ + 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); + + /* 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 %d more for a full request.\n", n, 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 %d: %s\n", + state->sock, 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 */ + + 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 %d: %s\n", + state->sock, 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, selret; + struct timeval timeout; + + /* Handle messages */ + + message_dispatch(); + + /* rescan the trusted domains list. This must be done + regularly to cope with transitive trusts */ + rescan_trusted_domains(False); + + /* Free up temporary memory */ + + lp_talloc_free(); + main_loop_talloc_free(); + + /* Initialise fd lists for select() */ + + listen_sock = open_winbindd_socket(); + + if (listen_sock == -1) { + perror("open_winbind_socket"); + exit(1); + } + + maxfd = listen_sock; + + FD_ZERO(&r_fds); + FD_ZERO(&w_fds); + FD_SET(listen_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_connection(listen_sock); + } + + /* 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 %d: %d bytes sent, should be %d\n", + state->request.pid, *(uint32 *) &state->request, sizeof(state->request))); + + 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")); + + /* Flush various caches */ + + flush_caches(); + reload_services_file(True); + do_sighup = False; + } + + if (do_sigusr2) { + print_winbindd_status(); + do_sigusr2 = False; + } + } +} + + +/* + these are split out from the main winbindd for use by the background daemon + */ +BOOL winbind_setup_common(void) +{ + load_interfaces(); + + if (!secrets_init()) { + + DEBUG(0,("Could not initialize domain trust account secrets. Giving up\n")); + return False; + } + + namecache_enable(); /* Enable netbios namecache */ + + /* Check winbindd parameters are valid */ + + ZERO_STRUCT(server_state); + + if (!winbindd_param_init()) + return False; + + /* Winbind daemon initialisation */ + + if (!winbindd_idmap_init()) + return False; + + /* 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); + + return True; +} + + +/* Main function */ + +struct winbindd_state server_state; /* Server state information */ + + +static void usage(void) +{ + printf("Usage: winbindd [options]\n"); + printf("\t-F daemon in foreground mode\n"); + printf("\t-S log to stdout\n"); + printf("\t-i interactive mode\n"); + printf("\t-B dual daemon mode\n"); + printf("\t-n disable cacheing\n"); + printf("\t-d level set debug level\n"); + printf("\t-s configfile choose smb.conf location\n"); + printf("\t-h show this help message\n"); +} + + int main(int argc, char **argv) +{ + extern BOOL AllowDebugChange; + pstring logfile; + BOOL interactive = False; + BOOL Fork = True; + BOOL log_stdout = False; + 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 ); + + snprintf(logfile, sizeof(logfile), "%s/log.winbindd", dyn_LOGFILEBASE); + lp_set_logfile(logfile); + + /* Initialise for running in non-root mode */ + + sec_init(); + + /* 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 */ + + while ((opt = getopt(argc, argv, "FSid:s:nhB")) != EOF) { + switch (opt) { + + case 'F': + Fork = False; + break; + case 'S': + log_stdout = True; + break; + /* Don't become a daemon */ + case 'i': + interactive = True; + log_stdout = True; + Fork = False; + break; + + /* dual daemon system */ + case 'B': + opt_dual_daemon = True; + break; + + /* disable cacheing */ + case 'n': + opt_nocache = True; + break; + + /* Run with specified debug level */ + case 'd': + DEBUGLEVEL = atoi(optarg); + AllowDebugChange = False; + break; + + /* Load a different smb.conf file */ + case 's': + pstrcpy(dyn_CONFIGFILE,optarg); + break; + + case 'h': + usage(); + exit(0); + + default: + printf("Unknown option %c\n", (char)opt); + exit(1); + } + } + + if (log_stdout && Fork) { + printf("Can't log to stdout (-S) unless daemon is in foreground +(-F) or interactive (-i)\n"); + usage(); + exit(1); + } + + snprintf(logfile, sizeof(logfile), "%s/log.winbindd", dyn_LOGFILEBASE); + lp_set_logfile(logfile); + setup_logging("winbindd", log_stdout); + reopen_logs(); + + DEBUG(1, ("winbindd version %s started.\n", VERSION ) ); + DEBUGADD( 1, ( "Copyright The Samba Team 2000-2001\n" ) ); + + if (!reload_services_file(False)) { + DEBUG(0, ("error opening config file\n")); + exit(1); + } + + /* Setup names. */ + + if (!init_names()) + exit(1); + + 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 (!winbind_setup_common()) { + return 1; + } + + if (opt_dual_daemon) { + do_dual_daemon(); + } + + /* Initialise messaging system */ + + if (!message_init()) { + DEBUG(0, ("unable to initialise messaging system\n")); + exit(1); + } + + register_msg_pool_usage(); + message_register(MSG_REQ_TALLOC_USAGE, return_all_talloc_info); + + /* Loop waiting for requests */ + + process_loop(); + + trustdom_cache_shutdown(); + uni_group_cache_shutdown(); + return 0; +} diff --git a/source4/nsswitch/winbindd.h b/source4/nsswitch/winbindd.h new file mode 100644 index 0000000000..42ef209faf --- /dev/null +++ b/source4/nsswitch/winbindd.h @@ -0,0 +1,230 @@ +/* + Unix SMB/CIFS implementation. + + Winbind daemon for ntdom nss module + + Copyright (C) Tim Potter 2000 + Copyright (C) Anthony Liguori 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 "includes.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) */ + 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 ? */ + + /* Lookup methods for this domain (LDAP or RPC) */ + + struct winbindd_methods *methods; + + /* Private data for the backends (used for connection cache) */ + + void *private; + + /* Sequence number stuff */ + + time_t last_seq_check; + uint32 sequence_number; + + /* 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, + 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, + 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, + 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, + 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 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 "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/source4/nsswitch/winbindd_ads.c b/source4/nsswitch/winbindd_ads.c new file mode 100644 index 0000000000..de3757aa44 --- /dev/null +++ b/source4/nsswitch/winbindd_ads.c @@ -0,0 +1,837 @@ +/* + Unix SMB/CIFS implementation. + + Winbind ADS backend functions + + Copyright (C) Andrew Tridgell 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "winbindd.h" + +#ifdef HAVE_ADS + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_WINBIND + +/* the realm of our primary LDAP server */ +static char *primary_realm; + + +/* + 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) { + return (ADS_STRUCT *)domain->private; + } + + /* 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(); + + if (primary_realm) { + SAFE_FREE(ads->auth.realm); + ads->auth.realm = strdup(primary_realm); + } + + status = ads_connect(ads); + if (!ADS_ERR_OK(status) || !ads->config.realm) { + extern struct winbindd_methods msrpc_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 == ADS_ERROR_SYSTEM && + status.err.rc == ECONNREFUSED) { + DEBUG(1,("Trying MSRPC methods\n")); + domain->methods = &msrpc_methods; + } + return NULL; + } + + /* remember our primary realm for trusted domain support */ + if (!primary_realm) { + primary_realm = strdup(ads->config.realm); + } + + 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) goto done; + + rc = ads_search_retry(ads, &res, "(objectCategory=user)", attrs); + if (!ADS_ERR_OK(rc)) { + 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) goto done; + + rc = ads_search_retry(ads, &res, "(objectCategory=group)", attrs); + if (!ADS_ERR_OK(rc)) { + 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; + if ( domain->native_mode ) + 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 + * ocal 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) + 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, + 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) + 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) +{ + char *exp; + void *res = NULL; + const char *attrs[] = {"userPrincipalName", "sAMAccountName", + "objectSid", "sAMAccountType", NULL}; + ADS_STATUS rc; + uint32 atype; + char *escaped_dn = escape_ldap_string_alloc(dn); + + if (!escaped_dn) { + return False; + } + + asprintf(&exp, "(distinguishedName=%s)", dn); + rc = ads_search_retry(ads, &res, exp, attrs); + SAFE_FREE(exp); + SAFE_FREE(escaped_dn); + + if (!ADS_ERR_OK(rc)) { + 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, + 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 *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) goto done; + + sidstr = sid_binstring(sid); + asprintf(&exp, "(objectSid=%s)", sidstr); + rc = ads_search_retry(ads, &msg, exp, attrs); + free(exp); + free(sidstr); + if (!ADS_ERR_OK(rc)) { + 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 *exp; + ADS_STRUCT *ads; + const char *group_attrs[] = {"objectSid", NULL}; + + ads = ads_cached_connection(domain); + if (!ads) goto done; + + /* buggy server, no tokenGroups. Instead lookup what groups this user + is a member of by DN search on member*/ + if (asprintf(&exp, "(&(member=%s)(objectClass=group))", user_dn) == -1) { + DEBUG(1,("lookup_usergroups(dn=%s) asprintf failed!\n", user_dn)); + return NT_STATUS_NO_MEMORY; + } + + rc = ads_search_retry(ads, &res, exp, group_attrs); + free(exp); + + if (!ADS_ERR_OK(rc)) { + 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); + if (msg) ads_msgfree(ads, msg); + + return status; +} + +/* Lookup groups a user is a member of. */ +static NTSTATUS lookup_usergroups(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + DOM_SID *sid, + uint32 *num_groups, DOM_SID ***user_gids) +{ + ADS_STRUCT *ads = NULL; + const char *attrs[] = {"distinguishedName", NULL}; + const char *attrs2[] = {"tokenGroups", "primaryGroupID", NULL}; + ADS_STATUS rc; + int count; + void *msg = NULL; + char *exp; + char *user_dn; + DOM_SID *sids; + int i; + DOM_SID *primary_group; + uint32 primary_group_rid; + char *sidstr; + fstring sid_string; + NTSTATUS status = NT_STATUS_UNSUCCESSFUL; + + DEBUG(3,("ads: lookup_usergroups\n")); + *num_groups = 0; + + ads = ads_cached_connection(domain); + if (!ads) goto done; + + if (!(sidstr = sid_binstring(sid))) { + DEBUG(1,("lookup_usergroups(sid=%s) sid_binstring returned NULL\n", sid_to_string(sid_string, sid))); + status = NT_STATUS_NO_MEMORY; + goto done; + } + if (asprintf(&exp, "(objectSid=%s)", sidstr) == -1) { + free(sidstr); + DEBUG(1,("lookup_usergroups(sid=%s) asprintf failed!\n", sid_to_string(sid_string, sid))); + status = NT_STATUS_NO_MEMORY; + goto done; + } + + rc = ads_search_retry(ads, &msg, exp, attrs); + free(exp); + free(sidstr); + + if (!ADS_ERR_OK(rc)) { + DEBUG(1,("lookup_usergroups(sid=%s) ads_search: %s\n", sid_to_string(sid_string, sid), ads_errstr(rc))); + goto done; + } + + user_dn = ads_pull_string(ads, mem_ctx, msg, "distinguishedName"); + if (!user_dn) { + DEBUG(1,("lookup_usergroups(sid=%s) ads_search did not return a a distinguishedName!\n", sid_to_string(sid_string, sid))); + if (msg) ads_msgfree(ads, msg); + goto done; + } + + if (msg) ads_msgfree(ads, msg); + + rc = ads_search_retry_dn(ads, &msg, user_dn, attrs2); + if (!ADS_ERR_OK(rc)) { + DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: %s\n", sid_to_string(sid_string, sid), ads_errstr(rc))); + 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;iprivate = 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) +{ + ADS_STRUCT *ads; + ADS_STATUS rc; + + *num_domains = 0; + *names = NULL; + + ads = ads_cached_connection(domain); + if (!ads) return NT_STATUS_UNSUCCESSFUL; + + rc = ads_trusted_domains(ads, mem_ctx, num_domains, names, alt_names, dom_sids); + + return ads_ntstatus(rc); +} + +/* find the domain sid for a domain */ +static NTSTATUS domain_sid(struct winbindd_domain *domain, DOM_SID *sid) +{ + ADS_STRUCT *ads; + ADS_STATUS rc; + + ads = ads_cached_connection(domain); + if (!ads) return NT_STATUS_UNSUCCESSFUL; + + rc = ads_domain_sid(ads, sid); + + if (!ADS_ERR_OK(rc)) { + /* its a dead connection */ + ads_destroy(&ads); + 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; + char *workgroup; + + ads = ads_cached_connection(domain); + if (!ads) 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(domain->alt_name); + strupper(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/source4/nsswitch/winbindd_cache.c b/source4/nsswitch/winbindd_cache.c new file mode 100644 index 0000000000..5fb59e7467 --- /dev/null +++ b/source4/nsswitch/winbindd_cache.c @@ -0,0 +1,1016 @@ +/* + Unix SMB/CIFS implementation. + + Winbind cache backend 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. +*/ + +#include "winbindd.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_WINBIND + +struct winbind_cache { + struct winbindd_methods *backend; + 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")); + } +} + +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) +{ + extern struct winbindd_methods msrpc_methods; + struct winbind_cache *ret = wcache; + + if (ret) return ret; + + ret = smb_xmalloc(sizeof(*ret)); + ZERO_STRUCTP(ret); + switch (lp_security()) { +#ifdef HAVE_ADS + case SEC_ADS: { + extern struct winbindd_methods ads_methods; + ret->backend = &ads_methods; + break; + } +#endif + default: + ret->backend = &msrpc_methods; + } + + 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) +{ + if (!wcache->tdb) return False; + return (domain->sequence_number == DOM_SEQUENCE_NONE); +} + + +/* + 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; + unsigned cache_time = lp_winbind_cache_time(); + + /* trying to reconnect is expensive, don't do it too often */ + if (domain->sequence_number == DOM_SEQUENCE_NONE) { + cache_time *= 8; + } + + time_diff = time(NULL) - domain->last_seq_check; + + /* see if we have to refetch the domain sequence number */ + if (!force && (time_diff < cache_time)) { + return; + } + + status = wcache->backend->sequence_number(domain, &domain->sequence_number); + + if (!NT_STATUS_IS_OK(status)) { + domain->sequence_number = DOM_SEQUENCE_NONE; + } + + domain->last_seq_check = time(NULL); +} + +/* + decide if a cache entry has expired +*/ +static BOOL centry_expired(struct winbindd_domain *domain, 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) { + 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) { + return False; + } + + /* 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); + free(kstr); + if (!data.dptr) { + /* a cache miss */ + return NULL; + } + + centry = smb_xmalloc(sizeof(*centry)); + centry->data = data.dptr; + centry->len = data.dsize; + centry->ofs = 0; + + if (centry->len < 8) { + /* huh? corrupt cache? */ + centry_free(centry); + return NULL; + } + + centry->status = NT_STATUS(centry_uint32(centry)); + centry->sequence_number = centry_uint32(centry); + + if (centry_expired(domain, centry)) { + extern BOOL opt_dual_daemon; + + if (opt_dual_daemon) { + extern BOOL background_process; + background_process = True; + } else { + centry_free(centry); + return NULL; + } + } + + 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) +{ + int len; + 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 = 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, DOM_SID *sid, + enum SID_NAME_USE type) +{ + struct cache_entry *centry; + uint32 len; + fstring uname; + fstring sid_string; + + centry = centry_start(domain, status); + if (!centry) return; + centry_put_sid(centry, sid); + fstrcpy(uname, name); + strupper(uname); + centry_end(centry, "NS/%s", sid_to_string(sid_string, sid)); + centry_free(centry); +} + +static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status, + 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)); + 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)); + 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; + + 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; + centry_free(centry); + return status; + +do_query: + *num_entries = 0; + *info = NULL; + + if (wcache_server_down(domain)) { + return NT_STATUS_SERVER_DISABLED; + } + + status = cache->backend->query_user_list(domain, mem_ctx, num_entries, info); + + /* and save it */ + refresh_sequence_number(domain, True); + 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 (cache->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; + centry_free(centry); + return status; + +do_query: + *num_entries = 0; + *info = NULL; + + if (wcache_server_down(domain)) { + return NT_STATUS_SERVER_DISABLED; + } + + status = cache->backend->enum_dom_groups(domain, mem_ctx, num_entries, info); + + /* and save it */ + refresh_sequence_number(domain, True); + 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, ("query_user_list: returning cached user list and server was down\n")); + status = NT_STATUS_MORE_PROCESSING_REQUIRED; + } else + status = centry->status; + + centry_free(centry); + return status; + +do_query: + *num_entries = 0; + *info = NULL; + + if (wcache_server_down(domain)) { + return NT_STATUS_SERVER_DISABLED; + } + + status = cache->backend->enum_local_groups(domain, mem_ctx, num_entries, info); + + /* and save it */ + refresh_sequence_number(domain, True); + 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(uname); + centry = wcache_fetch(cache, domain, "NS/%s/%s", domain->name, uname); + if (!centry) goto do_query; + *type = centry_uint32(centry); + sid2 = centry_sid(centry, mem_ctx); + if (!sid2) { + ZERO_STRUCTP(sid); + } else { + sid_copy(sid, sid2); + } + + status = centry->status; + centry_free(centry); + return status; + +do_query: + ZERO_STRUCTP(sid); + + if (wcache_server_down(domain)) { + return NT_STATUS_SERVER_DISABLED; + } + status = cache->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, + 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 = centry_uint32(centry); + *name = centry_string(centry, mem_ctx); + } + status = centry->status; + centry_free(centry); + return status; + +do_query: + *name = NULL; + + if (wcache_server_down(domain)) { + return NT_STATUS_SERVER_DISABLED; + } + status = cache->backend->sid_to_name(domain, mem_ctx, sid, name, type); + + /* and save it */ + refresh_sequence_number(domain, True); + 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, + DOM_SID *user_sid, + WINBIND_USERINFO *info) +{ + 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, "U/%s", sid_to_string(sid_string, user_sid)); + 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; + centry_free(centry); + return status; + +do_query: + ZERO_STRUCTP(info); + + if (wcache_server_down(domain)) { + return NT_STATUS_SERVER_DISABLED; + } + + status = cache->backend->query_user(domain, mem_ctx, user_sid, info); + + /* and save it */ + refresh_sequence_number(domain, True); + 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, + 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 (!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; + centry_free(centry); + return status; + +do_query: + (*num_groups) = 0; + (*user_gids) = NULL; + + if (wcache_server_down(domain)) { + return NT_STATUS_SERVER_DISABLED; + } + status = cache->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids); + + /* and save it */ + refresh_sequence_number(domain, True); + 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, + 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; + centry_free(centry); + return status; + +do_query: + (*num_names) = 0; + (*sid_mem) = NULL; + (*names) = NULL; + (*name_types) = NULL; + + + if (wcache_server_down(domain)) { + return NT_STATUS_SERVER_DISABLED; + } + status = cache->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names, + sid_mem, names, name_types); + + /* and save it */ + refresh_sequence_number(domain, True); + 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) +{ + struct winbind_cache *cache = get_cache(domain); + + /* we don't cache this call */ + return cache->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) +{ + struct winbind_cache *cache = get_cache(domain); + + /* we don't cache this call */ + return cache->backend->domain_sid(domain, sid); +} + +/* find the alternate names for the domain, if any */ +static NTSTATUS alternate_name(struct winbindd_domain *domain) +{ + struct winbind_cache *cache = get_cache(domain); + + /* we don't cache this call */ + return cache->backend->alternate_name(domain); +} + +/* 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/source4/nsswitch/winbindd_cm.c b/source4/nsswitch/winbindd_cm.c new file mode 100644 index 0000000000..0748a1c534 --- /dev/null +++ b/source4/nsswitch/winbindd_cm.c @@ -0,0 +1,954 @@ +/* + 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. + + - There needs to be a utility function in libsmb/namequery.c that does + cm_get_dc_name() + + - Take care when destroying cli_structs as they can be shared between + various sam handles. + + */ + +#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; + +/* Get a domain controller name. Cache positive and negative lookups so we + don't go to the network too often when something is badly broken. */ + +#define GET_DC_NAME_CACHE_TIMEOUT 30 /* Seconds between dc lookups */ + +struct get_dc_name_cache { + fstring domain_name; + fstring srv_name; + time_t lookup_time; + struct get_dc_name_cache *prev, *next; +}; + +/* + find the DC for a domain using methods appropriate for a ADS domain +*/ +static BOOL cm_ads_find_dc(const char *domain, struct in_addr *dc_ip, fstring srv_name) +{ + ADS_STRUCT *ads; + const char *realm = domain; + + if (strcasecmp(realm, lp_workgroup()) == 0) + realm = lp_realm(); + + ads = ads_init(realm, domain, NULL); + if (!ads) + return False; + + /* we don't need to bind, just connect */ + ads->auth.flags |= ADS_AUTH_NO_BIND; + + DEBUG(4,("cm_ads_find_dc: domain=%s\n", domain)); + +#ifdef HAVE_ADS + /* a full ads_connect() is actually overkill, as we don't srictly need + to do the SASL auth in order to get the info we need, but libads + doesn't offer a better way right now */ + ads_connect(ads); +#endif + + if (!ads->config.realm) + return False; + + fstrcpy(srv_name, ads->config.ldap_server_name); + strupper(srv_name); + *dc_ip = ads->ldap_ip; + ads_destroy(&ads); + + DEBUG(4,("cm_ads_find_dc: using server='%s' IP=%s\n", + srv_name, inet_ntoa(*dc_ip))); + + return True; +} + + + +static BOOL cm_get_dc_name(const char *domain, fstring srv_name, struct in_addr *ip_out) +{ + static struct get_dc_name_cache *get_dc_name_cache; + struct get_dc_name_cache *dcc; + struct in_addr dc_ip; + BOOL ret; + + /* Check the cache for previous lookups */ + + for (dcc = get_dc_name_cache; dcc; dcc = dcc->next) { + + if (!strequal(domain, dcc->domain_name)) + continue; /* Not our domain */ + + if ((time(NULL) - dcc->lookup_time) > + GET_DC_NAME_CACHE_TIMEOUT) { + + /* Cache entry has expired, delete it */ + + DEBUG(10, ("get_dc_name_cache entry expired for %s\n", domain)); + + DLIST_REMOVE(get_dc_name_cache, dcc); + SAFE_FREE(dcc); + + break; + } + + /* Return a positive or negative lookup for this domain */ + + if (dcc->srv_name[0]) { + DEBUG(10, ("returning positive get_dc_name_cache entry for %s\n", domain)); + fstrcpy(srv_name, dcc->srv_name); + return True; + } else { + DEBUG(10, ("returning negative get_dc_name_cache entry for %s\n", domain)); + return False; + } + } + + /* Add cache entry for this lookup. */ + + DEBUG(10, ("Creating get_dc_name_cache entry for %s\n", domain)); + + if (!(dcc = (struct get_dc_name_cache *) + malloc(sizeof(struct get_dc_name_cache)))) + return False; + + ZERO_STRUCTP(dcc); + + fstrcpy(dcc->domain_name, domain); + dcc->lookup_time = time(NULL); + + DLIST_ADD(get_dc_name_cache, dcc); + + zero_ip(&dc_ip); + + ret = False; + if (lp_security() == SEC_ADS) + ret = cm_ads_find_dc(domain, &dc_ip, srv_name); + + if (!ret) { + /* fall back on rpc methods if the ADS methods fail */ + ret = rpc_find_dc(domain, srv_name, &dc_ip); + } + + if (!ret) + return False; + + /* We have a name so make the cache entry positive now */ + fstrcpy(dcc->srv_name, srv_name); + + DEBUG(3, ("cm_get_dc_name: Returning DC %s (%s) for domain %s\n", srv_name, + inet_ntoa(dc_ip), domain)); + + *ip_out = dc_ip; + + return True; +} + +/* 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 new smb pipe connection to a DC on a given domain. Cache + negative creation attempts so we don't try and connect to broken + machines too often. */ + +#define FAILED_CONNECTION_CACHE_TIMEOUT 30 /* Seconds between attempts */ + +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; + +/* Add an entry to the failed conneciton cache */ + +static void add_failed_connection_entry(struct winbindd_cm_conn *new_conn, + NTSTATUS result) +{ + struct failed_connection_cache *fcc; + + SMB_ASSERT(!NT_STATUS_IS_OK(result)); + + /* Check we already aren't in the cache */ + + for (fcc = failed_connection_cache; fcc; fcc = fcc->next) { + if (strequal(fcc->domain_name, new_conn->domain)) { + DEBUG(10, ("domain %s already tried and failed\n", + fcc->domain_name)); + 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, new_conn->domain); + fstrcpy(fcc->controller, new_conn->controller); + fcc->lookup_time = time(NULL); + fcc->nt_status = result; + + DLIST_ADD(failed_connection_cache, fcc); +} + +/* Open a connction to the remote server, cache failures for 30 seconds */ + +static NTSTATUS cm_open_connection(const char *domain, const int pipe_index, + struct winbindd_cm_conn *new_conn, BOOL keep_mutex) +{ + struct failed_connection_cache *fcc; + NTSTATUS result; + char *ipc_username, *ipc_domain, *ipc_password; + struct in_addr dc_ip; + int i; + BOOL retry = True; + BOOL got_mutex = False; + + ZERO_STRUCT(dc_ip); + + fstrcpy(new_conn->domain, domain); + fstrcpy(new_conn->pipe_name, get_pipe_name_from_index(pipe_index)); + + /* Look for a domain controller for this domain. Negative results + are cached so don't bother applying the caching for this + function just yet. */ + + if (!cm_get_dc_name(domain, new_conn->controller, &dc_ip)) { + result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND; + add_failed_connection_entry(new_conn, result); + return result; + } + + /* Return false if we have tried to look up this domain and netbios + name before and failed. */ + + for (fcc = failed_connection_cache; fcc; fcc = fcc->next) { + + if (!(strequal(domain, fcc->domain_name) && + strequal(new_conn->controller, fcc->controller))) + continue; /* Not our domain */ + + if ((time(NULL) - fcc->lookup_time) > + FAILED_CONNECTION_CACHE_TIMEOUT) { + + /* Cache entry has expired, delete it */ + + DEBUG(10, ("cm_open_connection cache entry expired for %s, %s\n", domain, new_conn->controller)); + + DLIST_REMOVE(failed_connection_cache, fcc); + free(fcc); + + break; + } + + /* The timeout hasn't expired yet so return false */ + + DEBUG(10, ("returning negative open_connection_cache entry for %s, %s\n", domain, new_conn->controller)); + + result = fcc->nt_status; + SMB_ASSERT(!NT_STATUS_IS_OK(result)); + return result; + } + + /* Initialise SMB connection */ + + cm_get_ipc_userpass(&ipc_username, &ipc_domain, &ipc_password); + + DEBUG(5, ("connecting to %s from %s with username [%s]\\[%s]\n", + new_conn->controller, lp_netbios_name(), ipc_domain, ipc_username)); + + for (i = 0; retry && (i < 3); i++) { + + if (!secrets_named_mutex(new_conn->controller, WINBIND_SERVER_MUTEX_WAIT_TIME, &new_conn->mutex_ref_count)) { + DEBUG(0,("cm_open_connection: mutex grab failed for %s\n", new_conn->controller)); + result = NT_STATUS_POSSIBLE_DEADLOCK; + continue; + } + + got_mutex = True; + + result = cli_full_connection(&new_conn->cli, lp_netbios_name(), new_conn->controller, + &dc_ip, 0, "IPC$", "IPC", ipc_username, ipc_domain, + ipc_password, 0, &retry); + + if (NT_STATUS_IS_OK(result)) + break; + + secrets_named_mutex_release(new_conn->controller, &new_conn->mutex_ref_count); + got_mutex = False; + } + + SAFE_FREE(ipc_username); + SAFE_FREE(ipc_domain); + SAFE_FREE(ipc_password); + + if (!NT_STATUS_IS_OK(result)) { + if (got_mutex) + secrets_named_mutex_release(new_conn->controller, &new_conn->mutex_ref_count); + add_failed_connection_entry(new_conn, result); + return result; + } + + 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 (got_mutex) + secrets_named_mutex_release(new_conn->controller, &new_conn->mutex_ref_count); + if ( !is_win2k_pipe(pipe_index) ) + add_failed_connection_entry(new_conn, result); + cli_shutdown(new_conn->cli); + return result; + } + + if ((got_mutex) && !keep_mutex) + secrets_named_mutex_release(new_conn->controller, &new_conn->mutex_ref_count); + return NT_STATUS_OK; +} + +/* Return true if a connection is still alive */ + +static BOOL connection_ok(struct winbindd_cm_conn *conn) +{ + if (!conn) { + smb_panic("Invalid paramater passed to conneciton_ok(): conn was NULL!\n"); + return False; + } + + if (!conn->cli) { + DEBUG(0, ("Connection to %s for domain %s (pipe %s) has NULL conn->cli!\n", + conn->controller, conn->domain, conn->pipe_name)); + smb_panic("connection_ok: conn->cli was null!"); + return False; + } + + if (!conn->cli->initialised) { + DEBUG(0, ("Connection to %s for domain %s (pipe %s) was never initialised!\n", + conn->controller, conn->domain, conn->pipe_name)); + smb_panic("connection_ok: conn->cli->initialised is False!"); + 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; +} + +/* 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(const char *domain, const char *pipe_name, + struct winbindd_cm_conn **conn_out, BOOL keep_mutex) +{ + struct winbindd_cm_conn *conn, conn_temp; + NTSTATUS result; + + for (conn = cm_conns; conn; conn = conn->next) { + if (strequal(conn->domain, domain) && + strequal(conn->pipe_name, pipe_name)) { + if (!connection_ok(conn)) { + if (conn->cli) + cli_shutdown(conn->cli); + ZERO_STRUCT(conn_temp); + conn_temp.next = conn->next; + DLIST_REMOVE(cm_conns, conn); + SAFE_FREE(conn); + conn = &conn_temp; /* Just to keep the loop moving */ + } else { + if (keep_mutex) { + if (!secrets_named_mutex(conn->controller, + WINBIND_SERVER_MUTEX_WAIT_TIME, &conn->mutex_ref_count)) + DEBUG(0,("get_connection_from_cache: mutex grab failed for %s\n", + conn->controller)); + } + break; + } + } + } + + if (!conn) { + 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, keep_mutex))) { + DEBUG(3, ("Could not open a connection to %s for %s (%s)\n", + domain, pipe_name, nt_errstr(result))); + SAFE_FREE(conn); + return result; + } + DLIST_ADD(cm_conns, conn); + } + + *conn_out = conn; + return NT_STATUS_OK; +} + + +/********************************************************************************** +**********************************************************************************/ + +BOOL cm_check_for_native_mode_win2k( const char *domain ) +{ + NTSTATUS result; + struct winbindd_cm_conn conn; + DS_DOMINFO_CTR ctr; + BOOL ret = False; + + ZERO_STRUCT( conn ); + ZERO_STRUCT( ctr ); + + + if ( !NT_STATUS_IS_OK(result = cm_open_connection(domain, PI_LSARPC_DS, &conn, False)) ) { + DEBUG(5, ("cm_check_for_native_mode_win2k: Could not open a connection to %s for PIPE_LSARPC (%s)\n", + domain, nt_errstr(result))); + return False; + } + + if ( conn.cli ) { + if ( !NT_STATUS_IS_OK(cli_ds_getprimarydominfo( conn.cli, + conn.cli->mem_ctx, DsRolePrimaryDomainInfoBasic, &ctr)) ) { + ret = False; + goto done; + } + } + + if ( (ctr.basic->flags & DSROLE_PRIMARY_DS_RUNNING) + && !(ctr.basic->flags & DSROLE_PRIMARY_DS_MIXED_MODE) ) + ret = True; + +done: + if ( conn.cli ) + cli_shutdown( conn.cli ); + + return ret; +} + + + +/* Return a LSA policy handle on a domain */ + +CLI_POLICY_HND *cm_get_lsa_handle(const char *domain) +{ + 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, False))) + return NULL; + + /* This *shitty* code needs scrapping ! JRA */ + if (policy_handle_is_valid(&conn->pol)) { + hnd.pol = conn->pol; + hnd.cli = conn->cli; + return &hnd; + } + + 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, False))) + return NULL; + + 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 NULL; + } + } + + hnd.pol = conn->pol; + hnd.cli = conn->cli; + + return &hnd; +} + +/* Return a SAM policy handle on a domain */ + +CLI_POLICY_HND *cm_get_sam_handle(char *domain) +{ + 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, False))) + return NULL; + + /* This *shitty* code needs scrapping ! JRA */ + if (policy_handle_is_valid(&conn->pol)) { + hnd.pol = conn->pol; + hnd.cli = conn->cli; + return &hnd; + } + 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, False))) + return NULL; + + 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 NULL; + } + } + + hnd.pol = conn->pol; + hnd.cli = conn->cli; + + return &hnd; +} + +#if 0 /* This code now *well* out of date */ + +/* Return a SAM domain policy handle on a domain */ + +CLI_POLICY_HND *cm_get_sam_dom_handle(char *domain, DOM_SID *domain_sid) +{ + struct winbindd_cm_conn *conn, *basic_conn = NULL; + static CLI_POLICY_HND hnd; + NTSTATUS result; + uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED; + + /* Look for existing connections */ + + for (conn = cm_conns; conn; conn = conn->next) { + if (strequal(conn->domain, domain) && + strequal(conn->pipe_name, PIPE_SAMR) && + conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM) { + + if (!connection_ok(conn)) { + /* Shutdown cli? Free conn? Allow retry of DC? */ + DLIST_REMOVE(cm_conns, conn); + return NULL; + } + + goto ok; + } + } + + /* Create a basic handle to open a domain handle from */ + + if (!cm_get_sam_handle(domain)) + return False; + + for (conn = cm_conns; conn; conn = conn->next) { + if (strequal(conn->domain, domain) && + strequal(conn->pipe_name, PIPE_SAMR) && + conn->pipe_data.samr.pipe_type == SAM_PIPE_BASIC) + basic_conn = conn; + } + + if (!(conn = (struct winbindd_cm_conn *) + malloc(sizeof(struct winbindd_cm_conn)))) + return NULL; + + ZERO_STRUCTP(conn); + + fstrcpy(conn->domain, basic_conn->domain); + fstrcpy(conn->controller, basic_conn->controller); + fstrcpy(conn->pipe_name, basic_conn->pipe_name); + + conn->pipe_data.samr.pipe_type = SAM_PIPE_DOM; + conn->cli = basic_conn->cli; + + result = cli_samr_open_domain(conn->cli, conn->cli->mem_ctx, + &basic_conn->pol, des_access, + domain_sid, &conn->pol); + + if (!NT_STATUS_IS_OK(result)) + return NULL; + + /* Add to list */ + + DLIST_ADD(cm_conns, conn); + + ok: + hnd.pol = conn->pol; + hnd.cli = conn->cli; + + return &hnd; +} + +/* Return a SAM policy handle on a domain user */ + +CLI_POLICY_HND *cm_get_sam_user_handle(char *domain, DOM_SID *domain_sid, + uint32 user_rid) +{ + struct winbindd_cm_conn *conn, *basic_conn = NULL; + static CLI_POLICY_HND hnd; + NTSTATUS result; + uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED; + + /* Look for existing connections */ + + for (conn = cm_conns; conn; conn = conn->next) { + if (strequal(conn->domain, domain) && + strequal(conn->pipe_name, PIPE_SAMR) && + conn->pipe_data.samr.pipe_type == SAM_PIPE_USER && + conn->pipe_data.samr.rid == user_rid) { + + if (!connection_ok(conn)) { + /* Shutdown cli? Free conn? Allow retry of DC? */ + DLIST_REMOVE(cm_conns, conn); + return NULL; + } + + goto ok; + } + } + + /* Create a domain handle to open a user handle from */ + + if (!cm_get_sam_dom_handle(domain, domain_sid)) + return NULL; + + for (conn = cm_conns; conn; conn = conn->next) { + if (strequal(conn->domain, domain) && + strequal(conn->pipe_name, PIPE_SAMR) && + conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM) + basic_conn = conn; + } + + if (!basic_conn) { + DEBUG(0, ("No domain sam handle was created!\n")); + return NULL; + } + + if (!(conn = (struct winbindd_cm_conn *) + malloc(sizeof(struct winbindd_cm_conn)))) + return NULL; + + ZERO_STRUCTP(conn); + + fstrcpy(conn->domain, basic_conn->domain); + fstrcpy(conn->controller, basic_conn->controller); + fstrcpy(conn->pipe_name, basic_conn->pipe_name); + + conn->pipe_data.samr.pipe_type = SAM_PIPE_USER; + conn->cli = basic_conn->cli; + conn->pipe_data.samr.rid = user_rid; + + result = cli_samr_open_user(conn->cli, conn->cli->mem_ctx, + &basic_conn->pol, des_access, user_rid, + &conn->pol); + + if (!NT_STATUS_IS_OK(result)) + return NULL; + + /* Add to list */ + + DLIST_ADD(cm_conns, conn); + + ok: + hnd.pol = conn->pol; + hnd.cli = conn->cli; + + return &hnd; +} + +/* Return a SAM policy handle on a domain group */ + +CLI_POLICY_HND *cm_get_sam_group_handle(char *domain, DOM_SID *domain_sid, + uint32 group_rid) +{ + struct winbindd_cm_conn *conn, *basic_conn = NULL; + static CLI_POLICY_HND hnd; + NTSTATUS result; + uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED; + + /* Look for existing connections */ + + for (conn = cm_conns; conn; conn = conn->next) { + if (strequal(conn->domain, domain) && + strequal(conn->pipe_name, PIPE_SAMR) && + conn->pipe_data.samr.pipe_type == SAM_PIPE_GROUP && + conn->pipe_data.samr.rid == group_rid) { + + if (!connection_ok(conn)) { + /* Shutdown cli? Free conn? Allow retry of DC? */ + DLIST_REMOVE(cm_conns, conn); + return NULL; + } + + goto ok; + } + } + + /* Create a domain handle to open a user handle from */ + + if (!cm_get_sam_dom_handle(domain, domain_sid)) + return NULL; + + for (conn = cm_conns; conn; conn = conn->next) { + if (strequal(conn->domain, domain) && + strequal(conn->pipe_name, PIPE_SAMR) && + conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM) + basic_conn = conn; + } + + if (!basic_conn) { + DEBUG(0, ("No domain sam handle was created!\n")); + return NULL; + } + + if (!(conn = (struct winbindd_cm_conn *) + malloc(sizeof(struct winbindd_cm_conn)))) + return NULL; + + ZERO_STRUCTP(conn); + + fstrcpy(conn->domain, basic_conn->domain); + fstrcpy(conn->controller, basic_conn->controller); + fstrcpy(conn->pipe_name, basic_conn->pipe_name); + + conn->pipe_data.samr.pipe_type = SAM_PIPE_GROUP; + conn->cli = basic_conn->cli; + conn->pipe_data.samr.rid = group_rid; + + result = cli_samr_open_group(conn->cli, conn->cli->mem_ctx, + &basic_conn->pol, des_access, group_rid, + &conn->pol); + + if (!NT_STATUS_IS_OK(result)) + return NULL; + + /* Add to list */ + + DLIST_ADD(cm_conns, conn); + + ok: + hnd.pol = conn->pol; + hnd.cli = conn->cli; + + return &hnd; +} + +#endif + +/* 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(const char *domain, const unsigned char *trust_passwd, + struct cli_state **cli) +{ + NTSTATUS result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND; + struct winbindd_cm_conn *conn; + uint32 neg_flags = 0x000001ff; + + if (!cli) + return NT_STATUS_INVALID_PARAMETER; + + /* Open an initial conection - keep the mutex. */ + + if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_NETLOGON, &conn, True))) + return result; + + result = cli_nt_setup_creds(conn->cli, get_sec_chan(), trust_passwd, &neg_flags, 2); + + if (conn->mutex_ref_count) + secrets_named_mutex_release(conn->controller, &conn->mutex_ref_count); + + if (!NT_STATUS_IS_OK(result)) { + DEBUG(0, ("error connecting to domain password server: %s\n", + nt_errstr(result))); + + /* Hit the cache code again. This cleans out the old connection and gets a new one */ + if (conn->cli->fd == -1) { + + if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_NETLOGON, &conn, True))) + return result; + + /* Try again */ + result = cli_nt_setup_creds( conn->cli, get_sec_chan(),trust_passwd, &neg_flags, 2); + + if (conn->mutex_ref_count) + secrets_named_mutex_release(conn->controller, &conn->mutex_ref_count); + } + + 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")); +} diff --git a/source4/nsswitch/winbindd_dual.c b/source4/nsswitch/winbindd_dual.c new file mode 100644 index 0000000000..207757bcea --- /dev/null +++ b/source4/nsswitch/winbindd_dual.c @@ -0,0 +1,210 @@ +/* + 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 "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]); + + if (!winbind_setup_common()) + _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/source4/nsswitch/winbindd_group.c b/source4/nsswitch/winbindd_group.c new file mode 100644 index 0000000000..d06db5943c --- /dev/null +++ b/source4/nsswitch/winbindd_group.c @@ -0,0 +1,896 @@ +/* + Unix SMB/CIFS implementation. + + Winbind daemon for ntdom nss module + + Copyright (C) Tim Potter 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 "winbindd.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_WINBIND + +/*************************************************************** + 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; + + if (group_name_type != SID_NAME_DOM_GRP) { + DEBUG(1, ("SID %s in domain %s isn't a domain group\n", + sid_to_string(sid_string, group_sid), domain->name)); + 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. */ + + if (name_types[i] != SID_NAME_USER) { + DEBUG(3, ("name %s isn't a domain user\n", the_name)); + continue; + } + + /* Don't bother with machine accounts */ + + if (the_name[strlen(the_name) - 1] == '$') { + DEBUG(10, ("%s is machine account\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; + struct winbindd_domain *domain; + enum SID_NAME_USE name_type; + fstring name_domain, name_group; + char *tmp, *gr_mem; + gid_t gid; + int gr_mem_len; + + /* Ensure null termination */ + state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0'; + + DEBUG(3, ("[%5d]: getgrnam %s\n", state->pid, + state->request.data.groupname)); + + /* Parse domain and groupname */ + + memset(name_group, 0, sizeof(fstring)); + + tmp = state->request.data.groupname; + if (!parse_domain_user(tmp, name_domain, name_group)) + return WINBINDD_ERROR; + + /* Get info for the domain */ + + if ((domain = find_domain_from_name(name_domain)) == NULL) { + DEBUG(0, ("could not get domain sid for domain %s\n", + name_domain)); + 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_ALIAS) && (name_type != SID_NAME_DOM_GRP)) { + DEBUG(1, ("name '%s' is not a local or domain group: %d\n", + name_group, name_type)); + return WINBINDD_ERROR; + } + + if (!winbindd_idmap_get_gid_from_sid(&group_sid, &gid)) { + 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; + 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, ("[%5d]: getgrgid %d\n", state->pid, + 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; + + /* Get rid from gid */ + + if (!winbindd_idmap_get_sid_from_gid(state->request.data.gid, &group_sid)) { + DEBUG(1, ("could not convert gid %d to rid\n", + 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; + } + + if (!((name_type == SID_NAME_ALIAS) || + (name_type == SID_NAME_DOM_GRP))) { + DEBUG(1, ("name '%s' is not a local or domain group: %d\n", + group_name, name_type)); + 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 (!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, ("[%5d]: setgrent\n", 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; + + /* 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, ("[%5d]: endgrent\n", 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 */ + + if ( domain->native_mode && domain->methods->enum_local_groups ) + { + 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, ("[%5d]: getgrent\n", 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 (!winbindd_idmap_get_gid_from_sid( + &group_sid, + &group_gid)) { + + 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 %d for group %x\n", group_gid, + 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; + char *extra_data = NULL; + char *ted = NULL; + unsigned int extra_data_len = 0, i; + + DEBUG(3, ("[%5d]: list groups\n", state->pid)); + + /* Enumerate over trusted domains */ + + for (domain = domain_list(); domain; domain = domain->next) { + struct getent_state groups; + + ZERO_STRUCT(groups); + + /* Get list of sam groups */ + ZERO_STRUCT(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++] = ','; + } + + 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; +} + +/* Get user supplementary groups. This is much quicker than trying to + invert the groups database. */ + +enum winbindd_result winbindd_getgroups(struct winbindd_cli_state *state) +{ + fstring name_domain, name_user; + DOM_SID user_sid; + enum SID_NAME_USE name_type; + uint32 num_groups, num_gids; + NTSTATUS status; + DOM_SID **user_gids; + struct winbindd_domain *domain; + enum winbindd_result result = WINBINDD_ERROR; + gid_t *gid_list; + unsigned int i; + TALLOC_CTX *mem_ctx; + + /* Ensure null termination */ + state->request.data.username[sizeof(state->request.data.username)-1]='\0'; + + DEBUG(3, ("[%5d]: getgroups %s\n", 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 */ + + if (!parse_domain_user(state->request.data.username, name_domain, + name_user)) + goto done; + + /* Get info for the domain */ + + if ((domain = find_domain_from_name(name_domain)) == NULL) { + DEBUG(0, ("could not find domain entry for domain %s\n", + name_domain)); + goto done; + } + + /* 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) { + DEBUG(1, ("name '%s' is not a user name: %d\n", + name_user, name_type)); + goto done; + } + + status = domain->methods->lookup_usergroups(domain, mem_ctx, + &user_sid, &num_groups, + &user_gids); + if (!NT_STATUS_IS_OK(status)) goto done; + + /* Copy data back to client */ + + num_gids = 0; + gid_list = malloc(sizeof(gid_t) * num_groups); + + if (state->response.extra_data) + goto done; + + for (i = 0; i < num_groups; i++) { + if (!winbindd_idmap_get_gid_from_sid( + user_gids[i], + &gid_list[num_gids])) { + fstring sid_string; + + DEBUG(1, ("unable to convert group sid %s to gid\n", + sid_to_string(sid_string, user_gids[i]))); + continue; + } + + num_gids++; + } + + 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; +} diff --git a/source4/nsswitch/winbindd_idmap.c b/source4/nsswitch/winbindd_idmap.c new file mode 100644 index 0000000000..de547cde41 --- /dev/null +++ b/source4/nsswitch/winbindd_idmap.c @@ -0,0 +1,196 @@ +/* + Unix SMB/CIFS implementation. + Winbind ID Mapping + Copyright (C) Tim Potter 2000 + Copyright (C) Anthony Liguori 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 "winbindd.h" + +static struct { + const char *name; + /* Function to create a member of the idmap_methods list */ + BOOL (*reg_meth)(struct idmap_methods **methods); + struct idmap_methods *methods; +} builtin_idmap_functions[] = { + { "tdb", winbind_idmap_reg_tdb, NULL }, + /* { "ldap", winbind_idmap_reg_ldap, NULL },*/ + { NULL, NULL, NULL } +}; + +/* singleton pattern: uberlazy evaluation */ +static struct idmap_methods *impl; + +static struct idmap_methods *get_impl(const char *name) +{ + int i = 0; + struct idmap_methods *ret = NULL; + + while (builtin_idmap_functions[i].name && + strcmp(builtin_idmap_functions[i].name, name)) { + i++; + } + + if (builtin_idmap_functions[i].name) { + if (!builtin_idmap_functions[i].methods) { + builtin_idmap_functions[i].reg_meth(&builtin_idmap_functions[i].methods); + } + + ret = builtin_idmap_functions[i].methods; + } + + return ret; +} + +/* Initialize backend */ +BOOL winbindd_idmap_init(void) +{ + BOOL ret = False; + + DEBUG(3, ("winbindd_idmap_init: using '%s' as backend\n", + lp_idmap_backend())); + + if (!impl) { + impl = get_impl(lp_idmap_backend()); + if (!impl) { + DEBUG(0, ("winbindd_idmap_init: could not load backend '%s'\n", + lp_idmap_backend())); + } + } + + if (impl) { + ret = impl->init(); + } + + DEBUG(3, ("winbind_idmap_init: returning %s\n", ret ? "true" : "false")); + + return ret; +} + +/* Get UID from SID */ +BOOL winbindd_idmap_get_uid_from_sid(DOM_SID *sid, uid_t *uid) +{ + BOOL ret = False; + + if (!impl) { + impl = get_impl(lp_idmap_backend()); + if (!impl) { + DEBUG(0, ("winbindd_idmap_init: could not load backend '%s'\n", + lp_idmap_backend())); + } + } + + if (impl) { + ret = impl->get_uid_from_sid(sid, uid); + } + + return ret; +} + +/* Get GID from SID */ +BOOL winbindd_idmap_get_gid_from_sid(DOM_SID *sid, gid_t *gid) +{ + BOOL ret = False; + + if (!impl) { + impl = get_impl(lp_idmap_backend()); + if (!impl) { + DEBUG(0, ("winbindd_idmap_init: could not load backend '%s'\n", + lp_idmap_backend())); + } + } + + if (impl) { + ret = impl->get_gid_from_sid(sid, gid); + } + + return ret; +} + +/* Get SID from UID */ +BOOL winbindd_idmap_get_sid_from_uid(uid_t uid, DOM_SID *sid) +{ + BOOL ret = False; + + if (!impl) { + impl = get_impl(lp_idmap_backend()); + if (!impl) { + DEBUG(0, ("winbindd_idmap_init: could not load backend '%s'\n", + lp_idmap_backend())); + } + } + + if (impl) { + ret = impl->get_sid_from_uid(uid, sid); + } + + return ret; +} + +/* Get SID from GID */ +BOOL winbindd_idmap_get_sid_from_gid(gid_t gid, DOM_SID *sid) +{ + BOOL ret = False; + + if (!impl) { + impl = get_impl(lp_idmap_backend()); + } + + if (impl) { + ret = impl->get_sid_from_gid(gid, sid); + } else { + DEBUG(0, ("winbindd_idmap_init: could not load backend '%s'\n", + lp_idmap_backend())); + } + + return ret; +} + +/* Close backend */ +BOOL winbindd_idmap_close(void) +{ + BOOL ret = False; + + if (!impl) { + impl = get_impl(lp_idmap_backend()); + } + + if (impl) { + ret = impl->close(); + } else { + DEBUG(0, ("winbindd_idmap_init: could not load backend '%s'\n", + lp_idmap_backend())); + } + + return ret; +} + +/* Dump backend status */ +void winbindd_idmap_status(void) +{ + if (!impl) { + impl = get_impl(lp_idmap_backend()); + } + + if (impl) { + impl->status(); + } else { + DEBUG(0, ("winbindd_idmap_init: could not load backend '%s'\n", + lp_idmap_backend())); + } +} + diff --git a/source4/nsswitch/winbindd_idmap_tdb.c b/source4/nsswitch/winbindd_idmap_tdb.c new file mode 100644 index 0000000000..911b3b41d2 --- /dev/null +++ b/source4/nsswitch/winbindd_idmap_tdb.c @@ -0,0 +1,441 @@ +/* + Unix SMB/CIFS implementation. + + Winbind daemon - user related function + + Copyright (C) Tim Potter 2000 + Copyright (C) Anthony Liguori 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 "winbindd.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_WINBIND + +/* 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; + +/* convert one record to the new format */ +static int tdb_convert_fn(TDB_CONTEXT * tdb, TDB_DATA key, TDB_DATA data, + void *ignored) +{ + struct winbindd_domain *domain; + char *p; + DOM_SID sid; + uint32 rid; + fstring keystr; + fstring dom_name; + TDB_DATA key2; + + 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) { + /* We must delete the old record. */ + DEBUG(0, + ("winbindd: tdb_convert_fn : Unable to find domain %s\n", + dom_name)); + DEBUG(0, + ("winbindd: tdb_convert_fn : deleting record %s\n", + key.dptr)); + tdb_delete(idmap_tdb, key); + 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(idmap_tdb, key2, data, TDB_INSERT) != 0) { + /* not good! */ + DEBUG(0, + ("winbindd: tdb_convert_fn : Unable to update record %s\n", + key2.dptr)); + DEBUG(0, + ("winbindd: tdb_convert_fn : conversion failed - idmap corrupt ?\n")); + return -1; + } + + if (tdb_store(idmap_tdb, data, key2, TDB_REPLACE) != 0) { + /* not good! */ + DEBUG(0, + ("winbindd: tdb_convert_fn : Unable to update record %s\n", + data.dptr)); + DEBUG(0, + ("winbindd: tdb_convert_fn : conversion failed - idmap corrupt ?\n")); + return -1; + } + + tdb_delete(idmap_tdb, key); + + return 0; +} + +/***************************************************************************** + Convert the idmap database from an older version. +*****************************************************************************/ +static BOOL tdb_idmap_convert(const char *idmap_name) +{ + int32 vers = tdb_fetch_int32(idmap_tdb, "IDMAP_VERSION"); + BOOL bigendianheader = + (idmap_tdb->flags & TDB_BIGENDIAN) ? True : False; + + if (vers == IDMAP_VERSION) + return True; + + 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, + ("tdb_idmap_convert: Unable to byteswap user hwm in idmap database\n")); + 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, + ("tdb_idmap_convert: Unable to byteswap group hwm in idmap database\n")); + return False; + } + } + + /* the old format stored as DOMAIN/rid - now we store the SID direct */ + tdb_traverse(idmap_tdb, tdb_convert_fn, NULL); + + if (tdb_store_int32(idmap_tdb, "IDMAP_VERSION", IDMAP_VERSION) == + -1) { + DEBUG(0, + ("tdb_idmap_convert: Unable to byteswap group hwm in idmap database\n")); + return False; + } + + return True; +} + +/* Allocate either a user or group id from the pool */ +static BOOL tdb_allocate_id(uid_t * id, BOOL isgroup) +{ + int hwm; + + /* Get current high water mark */ + if ((hwm = tdb_fetch_int32(idmap_tdb, + isgroup ? HWM_GROUP : HWM_USER)) == + -1) { + return False; + } + + /* Return next available uid in list */ + if ((isgroup && (hwm > server_state.gid_high)) || + (!isgroup && (hwm > server_state.uid_high))) { + DEBUG(0, + ("winbind %sid range full!\n", isgroup ? "g" : "u")); + return False; + } + + if (id) { + *id = hwm; + } + + hwm++; + + /* Store new high water mark */ + tdb_store_int32(idmap_tdb, isgroup ? HWM_GROUP : HWM_USER, hwm); + + return True; +} + +/* Get a sid from an id */ +static BOOL tdb_get_sid_from_id(int id, DOM_SID * sid, BOOL isgroup) +{ + TDB_DATA key, data; + fstring keystr; + BOOL result = False; + + slprintf(keystr, sizeof(keystr), "%s %d", isgroup ? "GID" : "UID", + id); + + key.dptr = keystr; + key.dsize = strlen(keystr) + 1; + + data = tdb_fetch(idmap_tdb, key); + + if (data.dptr) { + result = string_to_sid(sid, data.dptr); + SAFE_FREE(data.dptr); + } + + return result; +} + +/* Get an id from a sid */ +static BOOL tdb_get_id_from_sid(DOM_SID * sid, uid_t * id, BOOL isgroup) +{ + TDB_DATA data, key; + fstring keystr; + BOOL result = False; + + /* Check if sid is present in database */ + sid_to_string(keystr, sid); + + key.dptr = keystr; + key.dsize = strlen(keystr) + 1; + + data = tdb_fetch(idmap_tdb, key); + + if (data.dptr) { + fstring scanstr; + int the_id; + + /* Parse and return existing uid */ + fstrcpy(scanstr, isgroup ? "GID" : "UID"); + fstrcat(scanstr, " %d"); + + if (sscanf(data.dptr, scanstr, &the_id) == 1) { + /* Store uid */ + if (id) { + *id = the_id; + } + + result = True; + } + + SAFE_FREE(data.dptr); + } else { + + /* Allocate a new id for this sid */ + if (id && tdb_allocate_id(id, isgroup)) { + fstring keystr2; + + /* Store new id */ + slprintf(keystr2, sizeof(keystr2), "%s %d", + isgroup ? "GID" : "UID", *id); + + data.dptr = keystr2; + data.dsize = strlen(keystr2) + 1; + + tdb_store(idmap_tdb, key, data, TDB_REPLACE); + tdb_store(idmap_tdb, data, key, TDB_REPLACE); + + result = True; + } + } + + return result; +} + +/***************************************************************************** + Initialise idmap database. +*****************************************************************************/ +static BOOL tdb_idmap_init(void) +{ + /* Open tdb cache */ + if (!(idmap_tdb = tdb_open_log(lock_path("winbindd_idmap.tdb"), 0, + TDB_DEFAULT, O_RDWR | O_CREAT, + 0600))) { + DEBUG(0, + ("winbindd_idmap_init: Unable to open idmap database\n")); + return False; + } + + /* possibly convert from an earlier version */ + if (!tdb_idmap_convert(lock_path("winbindd_idmap.tdb"))) { + DEBUG(0, + ("winbindd_idmap_init: Unable to open idmap database\n")); + return False; + } + + /* Create high water marks for group and user id */ + if (tdb_fetch_int32(idmap_tdb, HWM_USER) == -1) { + if (tdb_store_int32 + (idmap_tdb, HWM_USER, server_state.uid_low) == -1) { + DEBUG(0, + ("winbindd_idmap_init: Unable to initialise user hwm in idmap database\n")); + return False; + } + } + + if (tdb_fetch_int32(idmap_tdb, HWM_GROUP) == -1) { + if (tdb_store_int32 + (idmap_tdb, HWM_GROUP, server_state.gid_low) == -1) { + DEBUG(0, + ("winbindd_idmap_init: Unable to initialise group hwm in idmap database\n")); + return False; + } + } + + return True; +} + +/* Get a sid from a uid */ +static BOOL tdb_get_sid_from_uid(uid_t uid, DOM_SID * sid) +{ + return tdb_get_sid_from_id((int) uid, sid, False); +} + +/* Get a sid from a gid */ +static BOOL tdb_get_sid_from_gid(gid_t gid, DOM_SID * sid) +{ + return tdb_get_sid_from_id((int) gid, sid, True); +} + +/* Get a uid from a sid */ +static BOOL tdb_get_uid_from_sid(DOM_SID * sid, uid_t * uid) +{ + return tdb_get_id_from_sid(sid, uid, False); +} + +/* Get a gid from a group sid */ +static BOOL tdb_get_gid_from_sid(DOM_SID * sid, gid_t * gid) +{ + return tdb_get_id_from_sid(sid, gid, True); +} + +/* Close the tdb */ +static BOOL tdb_idmap_close(void) +{ + if (idmap_tdb) + return (tdb_close(idmap_tdb) == 0); + return True; +} + + +/* 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 tdb_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 - server_state.uid_low; + int total_users = + server_state.uid_high - server_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 - server_state.gid_low; + int total_groups = + server_state.gid_high - server_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 */ +} + +struct idmap_methods tdb_idmap_methods = { + tdb_idmap_init, + + tdb_get_sid_from_uid, + tdb_get_sid_from_gid, + + tdb_get_uid_from_sid, + tdb_get_gid_from_sid, + + tdb_idmap_close, + + tdb_idmap_status +}; + +BOOL winbind_idmap_reg_tdb(struct idmap_methods **meth) +{ + *meth = &tdb_idmap_methods; + + return True; +} diff --git a/source4/nsswitch/winbindd_misc.c b/source4/nsswitch/winbindd_misc.c new file mode 100644 index 0000000000..b85cd0570d --- /dev/null +++ b/source4/nsswitch/winbindd_misc.c @@ -0,0 +1,235 @@ +/* + 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 "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; + DEBUG(3, ("[%5d]: check machine account\n", state->pid)); + + /* Get trust account password */ + + again: + if (!secrets_fetch_trust_account_password( + lp_workgroup(), trust_passwd, NULL)) { + result = NT_STATUS_INTERNAL_ERROR; + 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(lp_workgroup(), trust_passwd, &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, ("[%5d]: list trusted domains\n", 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 (strequal(domain->name, lp_workgroup())) 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; + + DEBUG(3, ("[%5d]: show sequence\n", state->pid)); + + 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; + + 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_ping(struct winbindd_cli_state + *state) +{ + DEBUG(3, ("[%5d]: ping\n", state->pid)); + + return WINBINDD_OK; +} + +/* List various tidbits of information */ + +enum winbindd_result winbindd_info(struct winbindd_cli_state *state) +{ + + DEBUG(3, ("[%5d]: request misc info\n", state->pid)); + + state->response.data.info.winbind_separator = *lp_winbind_separator(); + fstrcpy(state->response.data.info.samba_version, VERSION); + + return WINBINDD_OK; +} + +/* Tell the client the current interface version */ + +enum winbindd_result winbindd_interface_version(struct winbindd_cli_state *state) +{ + + DEBUG(3, ("[%5d]: request interface version\n", 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, ("[%5d]: request domain name\n", 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, ("[%5d]: request netbios name\n", state->pid)); + + fstrcpy(state->response.data.netbios_name, lp_netbios_name()); + + return WINBINDD_OK; +} diff --git a/source4/nsswitch/winbindd_nss.h b/source4/nsswitch/winbindd_nss.h new file mode 100644 index 0000000000..2c87a77100 --- /dev/null +++ b/source4/nsswitch/winbindd_nss.h @@ -0,0 +1,242 @@ +/* + 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_DOMAIN_ENV "WINBINDD_DOMAIN" /* Environment variables */ +#define WINBINDD_DONT_ENV "_NO_WINBINDD" + +/* Update this when you change the interface. */ + +#define WINBIND_INTERFACE_VERSION 7 + +/* 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, + + /* 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_SHOW_SEQUENCE, /* display sequence numbers of domains */ + + /* WINS commands */ + + WINBINDD_WINS_BYIP, + WINBINDD_WINS_BYNAME, + + /* 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 */ + + WINBINDD_NUM_CMDS +}; + +#define WINBIND_PAM_INFO3_NDR 0x0001 +#define WINBIND_PAM_INFO3_TEXT 0x0002 +#define WINBIND_PAM_NTKEY 0x0004 +#define WINBIND_PAM_LMKEY 0x0008 +#define WINBIND_PAM_CONTACT_TRUSTDOM 0x0010 + +/* Winbind request structure */ + +struct winbindd_request { + uint32 length; + enum winbindd_cmd cmd; /* Winbindd command to execute */ + pid_t pid; /* pid of calling process */ + + 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; + uint32 flags; + } 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 */ + } 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 { + fstring pw_name; + fstring pw_passwd; + uid_t pw_uid; + gid_t pw_gid; + fstring pw_gecos; + fstring pw_dir; + fstring pw_shell; + } pw; + + /* getgrnam, getgrgid */ + + 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 */ + } 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; + } data; + + /* Variable length return data */ + + void *extra_data; /* getgrnam, getgrgid, getgrent */ +}; + +#endif diff --git a/source4/nsswitch/winbindd_pam.c b/source4/nsswitch/winbindd_pam.c new file mode 100644 index 0000000000..8a0326b42c --- /dev/null +++ b/source4/nsswitch/winbindd_pam.c @@ -0,0 +1,368 @@ +/* + 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 "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; +} + +/* Return a password structure from a username. */ + +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 smb_uid_low; + 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; + + /* 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, ("[%5d]: pam auth %s\n", 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 */ + + if (!parse_domain_user(state->request.data.auth.user, name_domain, + name_user)) { + DEBUG(5,("no domain separator (%s) in username (%s) - failing auth\n", lp_winbind_separator(), state->request.data.auth.user)); + result = NT_STATUS_INVALID_PARAMETER; + goto done; + } + + { + unsigned char local_lm_response[24]; + unsigned char local_nt_response[24]; + + 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)); + } + + /* + * Get the machine account password for our primary domain + */ + + if (!secrets_fetch_trust_account_password( + lp_workgroup(), trust_passwd, &last_change_time)) { + DEBUG(0, ("winbindd_pam_auth: could not fetch trust account " + "password for domain %s\n", lp_workgroup())); + result = NT_STATUS_CANT_ACCESS_DOMAIN_INFO; + goto done; + } + + /* We really don't care what LUID we give the user. */ + + generate_random_buffer( (unsigned char *)&smb_uid_low, 4, False); + + ZERO_STRUCT(info3); + + /* Don't shut this down - it belongs to the connection cache code */ + result = cm_get_netlogon_cli(lp_workgroup(), trust_passwd, &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, + name_user, name_domain, + lp_netbios_name(), chal, + lm_resp, nt_resp, + &info3); + + uni_group_cache_store_netlogon(mem_ctx, &info3); +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, 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 (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; + NET_USER_INFO_3 info3; + struct cli_state *cli = NULL; + TALLOC_CTX *mem_ctx = NULL; + char *user = NULL; + const char *domain = NULL; + const char *contact_domain; + const char *workstation; + + DATA_BLOB lm_resp, nt_resp; + + /* Ensure null termination */ + state->request.data.auth_crap.user[sizeof(state->request.data.auth_crap.user)-1]='\0'; + + /* Ensure null termination */ + 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, &user, state->request.data.auth_crap.user) == (size_t)-1) { + DEBUG(0, ("winbindd_pam_auth_crap: pull_utf8_talloc failed!\n")); + } + + 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")); + } + domain = dom; + } else if (lp_winbind_use_default_domain()) { + domain = lp_workgroup(); + } else { + DEBUG(5,("no domain specified with username (%s) - failing auth\n", + user)); + result = NT_STATUS_INVALID_PARAMETER; + goto done; + } + + DEBUG(3, ("[%5d]: pam auth crap domain: %s user: %s\n", state->pid, + domain, user)); + + if (lp_allow_trusted_domains() && (state->request.data.auth_crap.flags & WINBIND_PAM_CONTACT_TRUSTDOM)) { + contact_domain = domain; + } else { + contact_domain = lp_workgroup(); + } + + 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")); + } + workstation = wrk; + } else { + workstation = lp_netbios_name(); + } + + 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); + + /* + * Get the machine account password for the domain to contact. + * This is either our own domain for a workstation, or possibly + * any domain for a PDC with trusted domains. + */ + + if (!secrets_fetch_trust_account_password ( + contact_domain, trust_passwd, &last_change_time)) { + DEBUG(0, ("winbindd_pam_auth: could not fetch trust account " + "password for domain %s\n", contact_domain)); + result = NT_STATUS_CANT_ACCESS_DOMAIN_INFO; + goto done; + } + + ZERO_STRUCT(info3); + + /* Don't shut this down - it belongs to the connection cache code */ + result = cm_get_netlogon_cli(contact_domain, trust_passwd, &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, + user, domain, + workstation, state->request.data.auth_crap.chal, + lm_resp, nt_resp, + &info3); + + if (NT_STATUS_IS_OK(result)) { + uni_group_cache_store_netlogon(mem_ctx, &info3); + if (state->request.data.auth_crap.flags & WINBIND_PAM_INFO3_NDR) { + result = append_info3_as_ndr(mem_ctx, state, &info3); + } + +#if 0 + /* we don't currently do this stuff right */ + /* Doing an assert in a daemon is going to be a pretty bad + idea. - tpot */ + if (state->request.data.auth_crap.flags & WINBIND_PAM_NTKEY) { + SMB_ASSERT(sizeof(state->response.data.auth.nt_session_key) == sizeof(info3.user_sess_key)); + memcpy(state->response.data.auth.nt_session_key, info3.user_sess_key, sizeof(state->response.data.auth.nt_session_key) /* 16 */); + } + if (state->request.data.auth_crap.flags & WINBIND_PAM_LMKEY) { + SMB_ASSERT(sizeof(state->response.data.auth.nt_session_key) <= sizeof(info3.user_sess_key)); + memcpy(state->response.data.auth.first_8_lm_hash, info3.padding, sizeof(state->response.data.auth.nt_session_key) /* 16 */); + } +#endif + } + +done: + + state->response.data.auth.nt_status = NT_STATUS_V(result); + push_utf8_fstring(state->response.data.auth.nt_status_string, nt_errstr(result)); + push_utf8_fstring(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, + ("NTLM CRAP authentication 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; +} + +/* 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; + + DEBUG(3, ("[%5d]: pam chauthtok %s\n", state->pid, + state->request.data.chauthtok.user)); + + /* Setup crap */ + + if (state == NULL) + return WINBINDD_ERROR; + + if (!parse_domain_user(state->request.data.chauthtok.user, domain, + user)) { + result = NT_STATUS_INVALID_PARAMETER; + goto done; + } + + /* Change password */ + + oldpass = state->request.data.chauthtok.oldpass; + newpass = state->request.data.chauthtok.newpass; + + /* Get sam handle */ + + if (!(hnd = cm_get_sam_handle(domain))) { + DEBUG(1, ("could not get SAM handle on DC for %s\n", domain)); + result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND; + goto done; + } + + if (!cli_oem_change_password(hnd->cli, user, newpass, oldpass)) { + DEBUG(1, ("password change failed for user %s/%s\n", domain, + user)); + result = NT_STATUS_WRONG_PASSWORD; + } else { + result = NT_STATUS_OK; + } + +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)); + + return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR; +} diff --git a/source4/nsswitch/winbindd_rpc.c b/source4/nsswitch/winbindd_rpc.c new file mode 100644 index 0000000000..9989f27109 --- /dev/null +++ b/source4/nsswitch/winbindd_rpc.c @@ -0,0 +1,776 @@ +/* + 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 "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; + + DEBUG(3,("rpc: query_user_list\n")); + + *num_entries = 0; + *info = NULL; + + retry = 0; + do { + /* Get sam handle */ + + if (!(hnd = cm_get_sam_handle(domain->name))) + 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; + + i = start_idx = 0; + do { + TALLOC_CTX *ctx2; + char **dom_users; + uint32 num_dom_users, *dom_rids, j, size = 0xffff; + uint16 acb_mask = ACB_NORMAL; + + if (!(ctx2 = talloc_init("winbindd enum_users"))) { + result = NT_STATUS_NO_MEMORY; + goto done; + } + + result = cli_samr_enum_dom_users( + hnd->cli, ctx2, &dom_pol, &start_idx, acb_mask, + size, &dom_users, &dom_rids, &num_dom_users); + + *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++) { + (*info)[i].acct_name = + talloc_strdup(mem_ctx, dom_users[j]); + (*info)[i].full_name = talloc_strdup(mem_ctx, ""); + (*info)[i].user_sid = rid_to_talloced_sid(domain, mem_ctx, dom_rids[j]); + /* 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; + + *num_entries = 0; + *info = NULL; + + DEBUG(3,("rpc: enum_dom_groups\n")); + + retry = 0; + do { + if (!(hnd = cm_get_sam_handle(domain->name))) + return NT_STATUS_UNSUCCESSFUL; + + 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 ( !(hnd = cm_get_sam_handle(domain->name)) ) + return NT_STATUS_UNSUCCESSFUL; + + 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 status; + 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; + } + + retry = 0; + do { + if (!(hnd = cm_get_lsa_handle(domain->name))) { + return NT_STATUS_UNSUCCESSFUL; + } + + status = cli_lsa_lookup_names(hnd->cli, mem_ctx, &hnd->pol, 1, + &full_name, &sids, &types); + } while (!NT_STATUS_IS_OK(status) && (retry++ < 1) && hnd && hnd->cli && hnd->cli->fd == -1); + + /* Return rid and type if lookup successful */ + + if (NT_STATUS_IS_OK(status)) { + sid_copy(sid, &sids[0]); + *type = types[0]; + } + + return status; +} + +/* + convert a domain SID to a user or group name +*/ +static NTSTATUS sid_to_name(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + DOM_SID *sid, + char **name, + enum SID_NAME_USE *type) +{ + CLI_POLICY_HND *hnd; + char **domains; + char **names; + uint32 *types; + NTSTATUS status; + int retry; + + DEBUG(3,("rpc: sid_to_name\n")); + + retry = 0; + do { + if (!(hnd = cm_get_lsa_handle(domain->name))) + return NT_STATUS_UNSUCCESSFUL; + + status = cli_lsa_lookup_sids(hnd->cli, mem_ctx, &hnd->pol, + 1, sid, &domains, &names, &types); + } while (!NT_STATUS_IS_OK(status) && (retry++ < 1) && hnd && hnd->cli && hnd->cli->fd == -1); + + if (NT_STATUS_IS_OK(status)) { + *type = types[0]; + *name = names[0]; + DEBUG(5,("Mapped sid to [%s]\\[%s]\n", domains[0], *name)); + + /* Paranoia */ + if (strcasecmp(domain->name, domains[0]) != 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 status; +} + +/* Lookup user information from a rid or username. */ +static NTSTATUS query_user(struct winbindd_domain *domain, + TALLOC_CTX *mem_ctx, + DOM_SID *user_sid, + WINBIND_USERINFO *user_info) +{ + CLI_POLICY_HND *hnd; + 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; + + 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; + } + + retry = 0; + do { + /* Get sam handle */ + if (!(hnd = cm_get_sam_handle(domain->name))) + 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, + DOM_SID *user_sid, + uint32 *num_groups, DOM_SID ***user_gids) +{ + 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; + + DEBUG(3,("rpc: lookup_usergroups sid=%s\n", sid_to_string(sid_string, user_sid))); + + *num_groups = 0; + + /* First try cached universal groups from logon */ + *user_gids = uni_group_cache_fetch(&domain->sid, user_sid, mem_ctx, num_groups); + if((*num_groups > 0) && *user_gids) { + return NT_STATUS_OK; + } else { + *user_gids = NULL; + *num_groups = 0; + } + + retry = 0; + do { + /* Get sam handle */ + if (!(hnd = cm_get_sam_handle(domain->name))) + 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_gids) = talloc(mem_ctx, sizeof(uint32) * (*num_groups)); + if (!(*user_gids)) { + result = NT_STATUS_NO_MEMORY; + goto done; + } + + for (i=0;i<(*num_groups);i++) { + (*user_gids)[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, + DOM_SID *group_sid, uint32 *num_names, + DOM_SID ***sid_mem, char ***names, + uint32 **name_types) +{ + CLI_POLICY_HND *hnd; + 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 (!(hnd = cm_get_sam_handle(domain->name))) + 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; + + /* 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 (!*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); + + if (!NT_STATUS_IS_OK(result)) + 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; + + 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; +} + +/* 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; + uint32 seqnum = DOM_SEQUENCE_NONE; + 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 { + /* Get sam handle */ + if (!(hnd = cm_get_sam_handle(domain->name))) + 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)) { + seqnum = ctr.info.inf2.seq_num; + DEBUG(10,("domain_sequence_number: for domain %s is %u\n", domain->name, (unsigned)seqnum )); + } else { + DEBUG(10,("domain_sequence_number: failed to get sequence number (%u) for domain %s\n", + (unsigned)seqnum, domain->name )); + } + + done: + + if (got_dom_pol) + cli_samr_close(hnd->cli, mem_ctx, &dom_pol); + + talloc_destroy(mem_ctx); + + *seq = seqnum; + + 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 (!(hnd = cm_get_lsa_handle(lp_workgroup()))) + 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 status = NT_STATUS_UNSUCCESSFUL; + TALLOC_CTX *mem_ctx; + CLI_POLICY_HND *hnd; + fstring level5_dom; + 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 sam handle */ + if (!(hnd = cm_get_lsa_handle(domain->name))) + goto done; + + status = cli_lsa_query_info_policy(hnd->cli, mem_ctx, + &hnd->pol, 0x05, level5_dom, sid); + } while (!NT_STATUS_IS_OK(status) && (retry++ < 1) && hnd && hnd->cli && hnd->cli->fd == -1); + +done: + talloc_destroy(mem_ctx); + return status; +} + +/* 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/source4/nsswitch/winbindd_sid.c b/source4/nsswitch/winbindd_sid.c new file mode 100644 index 0000000000..6ab2eaa646 --- /dev/null +++ b/source4/nsswitch/winbindd_sid.c @@ -0,0 +1,235 @@ +/* + 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 "winbindd.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_WINBIND + +/* Convert a string */ + +enum winbindd_result winbindd_lookupsid(struct winbindd_cli_state *state) +{ + extern DOM_SID global_sid_Builtin; + enum SID_NAME_USE type; + DOM_SID sid, tmp_sid; + uint32 rid; + fstring name; + fstring dom_name; + + /* Ensure null termination */ + state->request.data.sid[sizeof(state->request.data.sid)-1]='\0'; + + DEBUG(3, ("[%5d]: lookupsid %s\n", 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; + } + + /* Don't look up BUILTIN sids */ + + sid_copy(&tmp_sid, &sid); + sid_split_rid(&tmp_sid, &rid); + + if (sid_equal(&tmp_sid, &global_sid_Builtin)) { + 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; + + /* 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'; + + DEBUG(3, ("[%5d]: lookupname %s%s%s\n", state->pid, + state->request.data.name.dom_name, + lp_winbind_separator(), + state->request.data.name.name)); + + name_domain = state->request.data.name.dom_name; + name_user = state->request.data.name.name; + + 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; + + /* Ensure null termination */ + state->request.data.sid[sizeof(state->request.data.sid)-1]='\0'; + + DEBUG(3, ("[%5d]: sid to uid %s\n", state->pid, + state->request.data.sid)); + + /* Split sid into domain sid and user rid */ + 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; + } + + /* Find uid for this sid and return it */ + if (!winbindd_idmap_get_uid_from_sid(&sid, &state->response.data.uid)) { + 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; + + /* Ensure null termination */ + state->request.data.sid[sizeof(state->request.data.sid)-1]='\0'; + + DEBUG(3, ("[%5d]: sid to gid %s\n", 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; + } + + /* Find gid for this sid and return it */ + if (!winbindd_idmap_get_gid_from_sid(&sid, &state->response.data.gid)) { + 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; + + /* 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, ("[%5d]: uid to sid %d\n", state->pid, + state->request.data.uid)); + + /* Lookup rid for this uid */ + if (!winbindd_idmap_get_sid_from_uid(state->request.data.uid, &sid)) { + DEBUG(1, ("Could not convert uid %d to rid\n", + state->request.data.uid)); + return WINBINDD_ERROR; + } + + 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; + + /* 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; + } + + DEBUG(3, ("[%5d]: gid to sid %d\n", state->pid, + state->request.data.gid)); + + /* Lookup sid for this uid */ + if (!winbindd_idmap_get_sid_from_gid(state->request.data.gid, &sid)) { + DEBUG(1, ("Could not convert gid %d to sid\n", + state->request.data.gid)); + return WINBINDD_ERROR; + } + + /* 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; +} diff --git a/source4/nsswitch/winbindd_user.c b/source4/nsswitch/winbindd_user.c new file mode 100644 index 0000000000..ee05543d30 --- /dev/null +++ b/source4/nsswitch/winbindd_user.c @@ -0,0 +1,607 @@ +/* + Unix SMB/CIFS implementation. + + Winbind daemon - user related functions + + Copyright (C) Tim Potter 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 "winbindd.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_WINBIND + +/* 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) +{ + extern userdom_struct current_user_info; + fstring output_username; + pstring homedir; + fstring sid_string; + + if (!pw || !dom_name || !user_name) + return False; + + /* Resolve the uid number */ + + if (!winbindd_idmap_get_uid_from_sid(user_sid, + &pw->pw_uid)) { + DEBUG(1, ("error getting user id for sid %s\n", sid_to_string(sid_string, user_sid))); + return False; + } + + /* Resolve the gid number */ + + if (!winbindd_idmap_get_gid_from_sid(group_sid, + &pw->pw_gid)) { + 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 lp_string() calling standard_sub_basic(). */ + + fstrcpy(current_user_info.smb_name, user_name); + sub_set_smb_name(user_name); + fstrcpy(current_user_info.domain, dom_name); + + pstrcpy(homedir, lp_template_homedir()); + + safe_strcpy(pw->pw_dir, homedir, sizeof(pw->pw_dir) - 1); + + safe_strcpy(pw->pw_shell, lp_template_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; + 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, ("[%5d]: getpwnam %s\n", state->pid, + state->request.data.username)); + + /* Parse domain and username */ + + if (!parse_domain_user(state->request.data.username, name_domain, + name_user)) + return WINBINDD_ERROR; + + if ((domain = find_domain_from_name(name_domain)) == NULL) { + DEBUG(5, ("no such domain: %s\n", name_domain)); + 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) { + DEBUG(1, ("name '%s' is not a user name: %d\n", name_user, + name_type)); + return WINBINDD_ERROR; + } + + /* Get some user info. Split the user rid from the sid obtained + from the winbind_lookup_by_name() call and use it in a + winbind_lookup_userinfo() */ + + 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; + fstring dom_name; + fstring user_name; + enum SID_NAME_USE name_type; + WINBIND_USERINFO user_info; + gid_t gid; + TALLOC_CTX *mem_ctx; + NTSTATUS status; + + /* 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, ("[%5d]: getpwuid %d\n", state->pid, + state->request.data.uid)); + + /* Get rid from uid */ + + if (!winbindd_idmap_get_sid_from_uid(state->request.data.uid, + &user_sid)) { + DEBUG(1, ("could not convert uid %d to SID\n", + 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(%d)", + 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; + } + + /* Resolve gid number */ + + if (!winbindd_idmap_get_gid_from_sid(user_info.group_sid, &gid)) { + 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, ("[%5d]: setpwent\n", 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; + } + + /* Create sam pipes for each domain we know about */ + + for(domain = domain_list(); domain != NULL; domain = domain->next) { + struct getent_state *domain_state; + + /* 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, ("[%5d]: endpwent\n", 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, ("[%5d]: getpwent\n", 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; + fstring domain_user_name; + 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; + + /* Skip machine accounts */ + + if (name_list[ent->sam_entry_index]. + name[strlen(name_list[ent->sam_entry_index].name) - 1] + == '$') { + ent->sam_entry_index++; + continue; + } + + /* 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", + domain_user_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; + 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, ("[%5d]: list users\n", state->pid)); + + if (!(mem_ctx = talloc_init("winbindd_list_users"))) + return WINBINDD_ERROR; + + /* Enumerate over trusted domains */ + + for (domain = domain_list(); domain; domain = domain->next) { + NTSTATUS status; + struct winbindd_methods *methods; + unsigned int i; + + 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/source4/nsswitch/winbindd_util.c b/source4/nsswitch/winbindd_util.c new file mode 100644 index 0000000000..7ccf032041 --- /dev/null +++ b/source4/nsswitch/winbindd_util.c @@ -0,0 +1,553 @@ +/* + Unix SMB/CIFS implementation. + + Winbind daemon for ntdom nss module + + Copyright (C) Tim Potter 2000-2001 + Copyright (C) 2001 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 "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 = ""; + +/* 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; + +struct winbindd_domain *domain_list(void) +{ + /* Initialise list */ + + if (!_domain_list) + init_domain_list(); + + 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; + } +} + + +/* 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; + + /* 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 (strcasecmp(domain_name, domain->name) == 0 || + strcasecmp(domain_name, domain->alt_name) == 0) { + return domain; + } + if (alt_name && *alt_name) { + if (strcasecmp(alt_name, domain->name) == 0 || + strcasecmp(alt_name, domain->alt_name) == 0) { + 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, '.') && alt_name && *alt_name) { + fstrcpy(domain->name, alt_name); + fstrcpy(domain->alt_name, domain_name); + } else { + fstrcpy(domain->name, domain_name); + if (alt_name) { + fstrcpy(domain->alt_name, alt_name); + } + } + + domain->methods = methods; + domain->sequence_number = DOM_SEQUENCE_NONE; + domain->last_seq_check = 0; + if (sid) { + sid_copy(&domain->sid, sid); + } + + /* see if this is a native mode win2k domain, but only for our own domain */ + + if ( strequal( lp_workgroup(), domain_name) ) { + domain->native_mode = cm_check_for_native_mode_win2k( domain_name ); + DEBUG(3,("add_trusted_domain: %s is a %s mode domain\n", domain_name, + domain->native_mode ? "native" : "mixed" )); + } + + /* Link to domain list */ + DLIST_ADD(_domain_list, domain); + + DEBUG(1,("Added domain %s %s %s\n", + domain->name, domain->alt_name, + sid?sid_string_static(&domain->sid):"")); + + return domain; +} + + +/* + rescan our domains looking for new trusted domains + */ +void rescan_trusted_domains(BOOL force) +{ + struct winbindd_domain *domain; + TALLOC_CTX *mem_ctx; + static time_t last_scan; + time_t t = time(NULL); + + /* trusted domains might be disabled */ + if (!lp_allow_trusted_domains()) { + return; + } + + /* Only rescan every few minutes but force if necessary */ + + if (((unsigned)(t - last_scan) < WINBINDD_RESCAN_FREQ) && !force) + return; + + last_scan = t; + + DEBUG(1, ("scanning trusted domain list\n")); + + if (!(mem_ctx = talloc_init("init_domain_list"))) + return; + + for (domain = _domain_list; domain; domain = domain->next) { + NTSTATUS result; + char **names; + char **alt_names; + int num_domains = 0; + DOM_SID *dom_sids; + int i; + + result = domain->methods->trusted_domains(domain, mem_ctx, &num_domains, + &names, &alt_names, &dom_sids); + if (!NT_STATUS_IS_OK(result)) { + continue; + } + + /* Add each domain to the trusted domain list. Each domain inherits + the access methods of its parent */ + 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]); + + /* store trusted domain in the cache */ + trustdom_cache_store(mem_ctx, names[i], alt_names ? alt_names[i] : NULL, + &dom_sids[i], t + WINBINDD_RESCAN_FREQ); + } + } + + talloc_destroy(mem_ctx); +} + +/* 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(), NULL, &cache_methods, NULL); + if (!secrets_fetch_domain_sid(domain->name, &domain->sid)) { + DEBUG(1, ("Could not fetch sid for our domain %s\n", + domain->name)); + return False; + } + + /* get any alternate name for the primary domain */ + cache_methods.alternate_name(domain); + + /* do an initial scan for trusted domains */ + rescan_trusted_domains(True); + + return True; +} + +/* Given a domain name, return the struct winbindd domain info for it + if it is actually 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; +} + +/* 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; + /* Don't bother with machine accounts */ + + if (name[strlen(name) - 1] == '$') + return False; + + 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_winbind_uid(&server_state.uid_low, &server_state.uid_high)) { + DEBUG(0, ("winbind uid range missing or invalid\n")); + return False; + } + + if (!lp_winbind_gid(&server_state.gid_low, &server_state.gid_high)) { + DEBUG(0, ("winbind gid range missing or invalid\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; +} + +/* 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 || lp_winbind_use_default_domain())) + return False; + + if(!p && lp_winbind_use_default_domain()) { + fstrcpy(user, domuser); + fstrcpy(domain, lp_workgroup()); + } else { + fstrcpy(user, p+1); + fstrcpy(domain, domuser); + domain[PTR_DIFF(p, domuser)] = 0; + } + strupper(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 + +*/ +void fill_domain_username(fstring name, const char *domain, const char *user) +{ + if(lp_winbind_use_default_domain() && + !strcmp(lp_workgroup(), domain)) { + strlcpy(name, user, sizeof(fstring)); + } else { + slprintf(name, sizeof(fstring) - 1, "%s%s%s", + domain, lp_winbind_separator(), + user); + } +} + +/* + * Winbindd socket accessor functions + */ + +/* Open the winbindd socket */ + +static int _winbindd_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; +} + +/* 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; + } +} + +/* + * 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; +} + diff --git a/source4/nsswitch/winbindd_wins.c b/source4/nsswitch/winbindd_wins.c new file mode 100644 index 0000000000..8ddd5dc10d --- /dev/null +++ b/source4/nsswitch/winbindd_wins.c @@ -0,0 +1,210 @@ +/* + 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 "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 in_addr *ret = NULL; + int j, flags = 0; + + *count = 0; + + /* always try with wins first */ + if (resolve_wins(name,0x20,&ret,count)) { + 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; +} + +/* 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, ("[%5d]: wins_byip %s\n", 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; + } + safe_strcat(response,state->request.data.winsreq,maxlen); + safe_strcat(response,"\t",maxlen); + 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; + } + safe_strcat(response, status[i].name, maxlen); + safe_strcat(response, " ", maxlen); + } + } + /* 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, ("[%5d]: wins_byname %s\n", 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 */ + response[strlen(response)-1] = ' '; + } + safe_strcat(response,addr,maxlen); + safe_strcat(response,"\t",maxlen); + } + size = strlen(state->request.data.winsreq) + strlen(response); + if (size > maxlen) { + SAFE_FREE(ip_list); + return WINBINDD_ERROR; + } + safe_strcat(response,state->request.data.winsreq,maxlen); + safe_strcat(response,"\n",maxlen); + SAFE_FREE(ip_list); + } else + return WINBINDD_ERROR; + + fstrcpy(state->response.data.winsresp,response); + + return WINBINDD_OK; +} diff --git a/source4/nsswitch/wins.c b/source4/nsswitch/wins.c new file mode 100644 index 0000000000..187748d285 --- /dev/null +++ b/source4/nsswitch/wins.c @@ -0,0 +1,322 @@ +/* + 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. + +*/ + +#include "includes.h" +#ifdef HAVE_NS_API_H +#undef VOLATILE + +#include +#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 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; +} + +static struct in_addr *lookup_byname_backend(const char *name, int *count) +{ + int fd = -1; + struct in_addr *ret = NULL; + struct in_addr p; + int j, flags = 0; + + if (!initialised) { + nss_wins_init(); + } + + *count = 0; + + /* always try with wins first */ + if (resolve_wins(name,0x20,&ret,count)) { + 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; + } + +out: + close(fd); + return ret; +} + + +#ifdef HAVE_NS_API_H +/* 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 +/**************************************************************************** +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 *name, struct hostent *he, + char *buffer, size_t buflen, int *errnop, + int *h_errnop) +{ + char **host_addresses; + struct in_addr *ip_list; + int i, count; + size_t namelen = strlen(name) + 1; + + memset(he, '\0', sizeof(*he)); + + ip_list = lookup_byname_backend(name, &count); + if (!ip_list) { + return NSS_STATUS_NOTFOUND; + } + + if (buflen < namelen + (2*count+1)*INADDRSZ) { + /* no ENOMEM error type?! */ + return NSS_STATUS_NOTFOUND; + } + + + host_addresses = (char **)buffer; + he->h_addr_list = host_addresses; + host_addresses[count] = NULL; + buffer += (count + 1) * INADDRSZ; + buflen += (count + 1) * INADDRSZ; + he->h_addrtype = AF_INET; + he->h_length = INADDRSZ; + + for (i=0;ih_name = buffer; + + return NSS_STATUS_SUCCESS; +} + + +NSS_STATUS +_nss_wins_gethostbyname2_r(const char *name, int af, struct hostent *he, + char *buffer, size_t buflen, int *errnop, + int *h_errnop) +{ + if(af!=AF_INET) { + *h_errnop = NO_DATA; + *errnop = EAFNOSUPPORT; + return NSS_STATUS_UNAVAIL; + } + + return _nss_wins_gethostbyname_r(name,he,buffer,buflen,errnop,h_errnop); +} +#endif diff --git a/source4/ntvfs/README b/source4/ntvfs/README new file mode 100644 index 0000000000..c86c9a0050 --- /dev/null +++ b/source4/ntvfs/README @@ -0,0 +1,26 @@ +This is the base of the new NTVFS subsystem for Samba. The model for +NTVFS backends is quite different than for the older style VFS +backends, in particular: + +- the NTVFS backends receive windows style file names, although they + are in the unix charset (usually UTF8). This means the backend is + responsible for mapping windows filename conventions to unix + filename conventions if necessary + +- the NTVFS backends are responsible for changing effective UID before + calling any OS local filesystem operations (if needed). The + become_*() functions are provided to make this easier. + +- the NTVFS backends are responsible for resolving DFS paths + +- each NTVFS backend handles either disk, printer or IPC$ shares, + rather than one backend handling all types + +- the entry points of the NTVFS backends correspond closely with basic + SMB operations, wheres the old VFS was modelled directly on the + POSIX filesystem interface. + +- the NTVFS backends are responsible for all semantic mappings, such + as mapping dos file attributes, ACLs, file ownership and file times + + diff --git a/source4/ntvfs/cifs/README b/source4/ntvfs/cifs/README new file mode 100644 index 0000000000..a43ad09bdf --- /dev/null +++ b/source4/ntvfs/cifs/README @@ -0,0 +1,5 @@ +This is the 'CIFS on CIFS' backend for Samba. It provides a NTVFS +backend that talks to a remote CIFS server. The primary aim of this +backend is for debugging and development, although some poeple may +find it useful as a CIFS gateway. + diff --git a/source4/ntvfs/cifs/vfs_cifs.c b/source4/ntvfs/cifs/vfs_cifs.c new file mode 100644 index 0000000000..9a17336519 --- /dev/null +++ b/source4/ntvfs/cifs/vfs_cifs.c @@ -0,0 +1,723 @@ +/* + Unix SMB/CIFS implementation. + + CIFS-on-CIFS NTVFS filesystem backend + + Copyright (C) Andrew Tridgell 2003 + Copyright (C) James J Myers 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the 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 implements a CIFS->CIFS NTVFS filesystem backend. + +*/ + +#include "includes.h" + +/* this is stored in ntvfs_private */ +struct cvfs_private { + struct cli_tree *tree; + struct cli_transport *transport; + struct tcon_context *conn; + const char *map_calls; +}; + + +/* a structure used to pass information to an async handler */ +struct async_info { + struct request_context *req; + void *parms; +}; + +/* + an idle function to cope with messages from the smbd client while + waiting for a reply from the server + this function won't be needed once all of the cifs backend + and the core of smbd is converted to use async calls +*/ +static void idle_func(struct cli_transport *transport, void *p_private) +{ + struct cvfs_private *private = p_private; + if (socket_pending(private->conn->smb->socket.fd)) { + smbd_process_async(private->conn->smb); + } +} + +/* + a handler for oplock break events from the server - these need to be passed + along to the client + */ +static BOOL oplock_handler(struct cli_transport *transport, uint16 tid, uint16 fnum, uint8 level, void *p_private) +{ + struct cvfs_private *private = p_private; + + DEBUG(5,("vfs_cifs: sending oplock break level %d for fnum %d\n", level, fnum)); + return req_send_oplock_break(private->conn, fnum, level); +} + +/* + a handler for read events on a connection to a backend server +*/ +static void cifs_socket_handler(struct event_context *ev, struct fd_event *fde, time_t t, uint16 flags) +{ + struct tcon_context *conn = fde->private; + struct cvfs_private *private = conn->ntvfs_private; + + DEBUG(5,("cifs_socket_handler event on fd %d\n", fde->fd)); + + if (!cli_request_receive_next(private->transport)) { + /* the connection to our server is dead */ + close_cnum(conn); + } +} + +/* + connect to a share - used when a tree_connect operation comes in. +*/ +static NTSTATUS cvfs_connect(struct request_context *req, const char *sharename) +{ + struct tcon_context *conn = req->conn; + NTSTATUS status; + struct cvfs_private *private; + char *map_calls; + struct fd_event fde; + const char *host, *user, *pass, *domain, *remote_share; + + /* Here we need to determine which server to connect to. + * For now we use parametric options, type cifs. + * Later we will use security=server and auth_server.c. + */ + host = lp_parm_string(req->conn->service, "cifs", "server"); + user = lp_parm_string(req->conn->service, "cifs", "user"); + pass = lp_parm_string(req->conn->service, "cifs", "password"); + domain = lp_parm_string(req->conn->service, "cifs", "domain"); + remote_share = lp_parm_string(req->conn->service, "cifs", "share"); + if (!remote_share) { + remote_share = sharename; + } + + if (!host || !user || !pass || !domain) { + DEBUG(1,("CIFS backend: You must supply server, user, password and domain\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + private = talloc(req->conn->mem_ctx, sizeof(struct cvfs_private)); + if (!private) { + return NT_STATUS_NO_MEMORY; + } + ZERO_STRUCTP(private); + + req->conn->ntvfs_private = (void *)private; + + status = cli_tree_full_connection(&private->tree, + "vfs_cifs", + host, + 0, + remote_share, "?????", + user, domain, + pass); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + private->transport = private->tree->session->transport; + private->tree->session->pid = SVAL(req->in.hdr, HDR_PID); + private->conn = req->conn; + + conn->fs_type = talloc_strdup(conn->mem_ctx, "NTFS"); + conn->dev_type = talloc_strdup(conn->mem_ctx, "A:"); + + map_calls = lp_parm_string(req->conn->service, "cifs", "map calls"); + if (map_calls) { + private->map_calls = talloc_strdup(conn->mem_ctx, map_calls); + } + + /* if we are mapping trans2, then we need to not give a trans2 + pointer in the operations structure */ + if (private->map_calls && in_list("trans2", private->map_calls, True)) { + conn->ntvfs_ops->trans2 = NULL; + } + + /* we need to tell the event loop that we wish to receive read events + on our SMB connection to the server */ + fde.fd = private->transport->socket->fd; + fde.flags = EVENT_FD_READ; + fde.private = req->conn; + fde.handler = cifs_socket_handler; + + event_add_fd(conn->smb->events, &fde); + + /* we need to receive oplock break requests from the server */ + cli_oplock_handler(private->transport, oplock_handler, private); + cli_transport_idle_handler(private->transport, idle_func, 100, private); + + return NT_STATUS_OK; +} + +/* + disconnect from a share +*/ +static NTSTATUS cvfs_disconnect(struct tcon_context *conn) +{ + struct cvfs_private *private = conn->ntvfs_private; + + event_remove_fd_all(conn->smb->events, private->transport->socket->fd); + smb_tree_disconnect(private->tree); + cli_tree_close(private->tree); + + return NT_STATUS_OK; +} + +/* + a handler for simple async replies + this handler can only be used for functions that don't return any + parameters (those that just return a status code) + */ +static void async_simple(struct cli_request *c_req) +{ + struct async_info *async = c_req->async.private; + struct request_context *req = async->req; + req->async.status = cli_request_simple_recv(c_req); + req->async.send_fn(req); +} + + +/* save some typing for the simple functions */ +#define ASYNC_RECV_TAIL(io, async_fn) do { \ + if (!c_req) return NT_STATUS_UNSUCCESSFUL; \ + { \ + struct async_info *async = c_req->async.private; \ + async = talloc(req->mem_ctx, sizeof(*async)); \ + if (!async) return NT_STATUS_NO_MEMORY; \ + async->parms = io; \ + async->req = req; \ + c_req->async.private = async; \ + } \ + c_req->async.fn = async_fn; \ + req->control_flags |= REQ_CONTROL_ASYNC; \ + return NT_STATUS_OK; \ +} while (0) + +#define SIMPLE_ASYNC_TAIL ASYNC_RECV_TAIL(NULL, async_simple) + +/* + delete a file - the dirtype specifies the file types to include in the search. + The name can contain CIFS wildcards, but rarely does (except with OS/2 clients) +*/ +static NTSTATUS cvfs_unlink(struct request_context *req, struct smb_unlink *unl) +{ + struct cvfs_private *private = req->conn->ntvfs_private; + struct cli_request *c_req; + + /* see if the front end will allow us to perform this + function asynchronously. */ + if (!req->async.send_fn) { + return smb_raw_unlink(private->tree, unl); + } + + c_req = smb_raw_unlink_send(private->tree, unl); + + SIMPLE_ASYNC_TAIL; +} + +/* + a handler for async ioctl replies + */ +static void async_ioctl(struct cli_request *c_req) +{ + struct async_info *async = c_req->async.private; + struct request_context *req = async->req; + req->async.status = smb_raw_ioctl_recv(c_req, req->mem_ctx, async->parms); + req->async.send_fn(req); +} + +/* + ioctl interface +*/ +static NTSTATUS cvfs_ioctl(struct request_context *req, struct smb_ioctl *io) +{ + struct cvfs_private *private = req->conn->ntvfs_private; + struct cli_request *c_req; + + /* see if the front end will allow us to perform this + function asynchronously. */ + if (!req->async.send_fn) { + return smb_raw_ioctl(private->tree, req->mem_ctx, io); + } + + c_req = smb_raw_ioctl_send(private->tree, io); + + ASYNC_RECV_TAIL(io, async_ioctl); +} + +/* + check if a directory exists +*/ +static NTSTATUS cvfs_chkpath(struct request_context *req, struct smb_chkpath *cp) +{ + struct cvfs_private *private = req->conn->ntvfs_private; + struct cli_request *c_req; + + if (!req->async.send_fn) { + return smb_raw_chkpath(private->tree, cp); + } + + c_req = smb_raw_chkpath_send(private->tree, cp); + + SIMPLE_ASYNC_TAIL; +} + +/* + a handler for async qpathinfo replies + */ +static void async_qpathinfo(struct cli_request *c_req) +{ + struct async_info *async = c_req->async.private; + struct request_context *req = async->req; + req->async.status = smb_raw_pathinfo_recv(c_req, req->mem_ctx, async->parms); + req->async.send_fn(req); +} + +/* + return info on a pathname +*/ +static NTSTATUS cvfs_qpathinfo(struct request_context *req, union smb_fileinfo *info) +{ + struct cvfs_private *private = req->conn->ntvfs_private; + struct cli_request *c_req; + + if (!req->async.send_fn) { + return smb_raw_pathinfo(private->tree, req->mem_ctx, info); + } + + c_req = smb_raw_pathinfo_send(private->tree, info); + + ASYNC_RECV_TAIL(info, async_qpathinfo); +} + +/* + a handler for async qfileinfo replies + */ +static void async_qfileinfo(struct cli_request *c_req) +{ + struct async_info *async = c_req->async.private; + struct request_context *req = async->req; + req->async.status = smb_raw_fileinfo_recv(c_req, req->mem_ctx, async->parms); + req->async.send_fn(req); +} + +/* + query info on a open file +*/ +static NTSTATUS cvfs_qfileinfo(struct request_context *req, union smb_fileinfo *info) +{ + struct cvfs_private *private = req->conn->ntvfs_private; + struct cli_request *c_req; + + if (!req->async.send_fn) { + return smb_raw_fileinfo(private->tree, req->mem_ctx, info); + } + + c_req = smb_raw_fileinfo_send(private->tree, info); + + ASYNC_RECV_TAIL(info, async_qfileinfo); +} + + +/* + set info on a pathname +*/ +static NTSTATUS cvfs_setpathinfo(struct request_context *req, union smb_setfileinfo *st) +{ + struct cvfs_private *private = req->conn->ntvfs_private; + struct cli_request *c_req; + + if (!req->async.send_fn) { + return smb_raw_setpathinfo(private->tree, st); + } + + c_req = smb_raw_setpathinfo_send(private->tree, st); + + SIMPLE_ASYNC_TAIL; +} + + +/* + a handler for async open replies + */ +static void async_open(struct cli_request *c_req) +{ + struct async_info *async = c_req->async.private; + struct request_context *req = async->req; + req->async.status = smb_raw_open_recv(c_req, req->mem_ctx, async->parms); + req->async.send_fn(req); +} + +/* + open a file +*/ +static NTSTATUS cvfs_open(struct request_context *req, union smb_open *io) +{ + struct cvfs_private *private = req->conn->ntvfs_private; + struct cli_request *c_req; + + if (private->map_calls && in_list("open", private->map_calls, True) && + io->generic.level != RAW_OPEN_GENERIC) { + return ntvfs_map_open(req, io); + } + + if (!req->async.send_fn) { + return smb_raw_open(private->tree, req->mem_ctx, io); + } + + c_req = smb_raw_open_send(private->tree, io); + + ASYNC_RECV_TAIL(io, async_open); +} + +/* + create a directory +*/ +static NTSTATUS cvfs_mkdir(struct request_context *req, union smb_mkdir *md) +{ + struct cvfs_private *private = req->conn->ntvfs_private; + struct cli_request *c_req; + + if (!req->async.send_fn) { + return smb_raw_mkdir(private->tree, md); + } + + c_req = smb_raw_mkdir_send(private->tree, md); + + SIMPLE_ASYNC_TAIL; +} + +/* + remove a directory +*/ +static NTSTATUS cvfs_rmdir(struct request_context *req, struct smb_rmdir *rd) +{ + struct cvfs_private *private = req->conn->ntvfs_private; + struct cli_request *c_req; + + if (!req->async.send_fn) { + return smb_raw_rmdir(private->tree, rd); + } + c_req = smb_raw_rmdir_send(private->tree, rd); + + SIMPLE_ASYNC_TAIL; +} + +/* + rename a set of files +*/ +static NTSTATUS cvfs_rename(struct request_context *req, struct smb_rename *ren) +{ + struct cvfs_private *private = req->conn->ntvfs_private; + struct cli_request *c_req; + + if (!req->async.send_fn) { + return smb_raw_rename(private->tree, ren); + } + + c_req = smb_raw_rename_send(private->tree, ren); + + SIMPLE_ASYNC_TAIL; +} + +/* + copy a set of files +*/ +static NTSTATUS cvfs_copy(struct request_context *req, struct smb_copy *cp) +{ + return NT_STATUS_NOT_SUPPORTED; +} + +/* + a handler for async read replies + */ +static void async_read(struct cli_request *c_req) +{ + struct async_info *async = c_req->async.private; + struct request_context *req = async->req; + req->async.status = smb_raw_read_recv(c_req, async->parms); + req->async.send_fn(req); +} + +/* + read from a file +*/ +static NTSTATUS cvfs_read(struct request_context *req, union smb_read *rd) +{ + struct cvfs_private *private = req->conn->ntvfs_private; + struct cli_request *c_req; + + if (!req->async.send_fn) { + return smb_raw_read(private->tree, rd); + } + + c_req = smb_raw_read_send(private->tree, rd); + + ASYNC_RECV_TAIL(rd, async_read); +} + +/* + a handler for async write replies + */ +static void async_write(struct cli_request *c_req) +{ + struct async_info *async = c_req->async.private; + struct request_context *req = async->req; + req->async.status = smb_raw_write_recv(c_req, async->parms); + req->async.send_fn(req); +} + +/* + write to a file +*/ +static NTSTATUS cvfs_write(struct request_context *req, union smb_write *wr) +{ + struct cvfs_private *private = req->conn->ntvfs_private; + struct cli_request *c_req; + + if (!req->async.send_fn) { + return smb_raw_write(private->tree, wr); + } + + c_req = smb_raw_write_send(private->tree, wr); + + ASYNC_RECV_TAIL(wr, async_write); +} + +/* + seek in a file +*/ +static NTSTATUS cvfs_seek(struct request_context *req, struct smb_seek *io) +{ + return NT_STATUS_NOT_SUPPORTED; +} + +/* + flush a file +*/ +static NTSTATUS cvfs_flush(struct request_context *req, struct smb_flush *io) +{ + return NT_STATUS_OK; +} + +/* + close a file +*/ +static NTSTATUS cvfs_close(struct request_context *req, union smb_close *io) +{ + struct cvfs_private *private = req->conn->ntvfs_private; + struct cli_request *c_req; + + if (!req->async.send_fn) { + return smb_raw_close(private->tree, io); + } + + c_req = smb_raw_close_send(private->tree, io); + + SIMPLE_ASYNC_TAIL; +} + +/* + exit - closing files? +*/ +static NTSTATUS cvfs_exit(struct request_context *req) +{ + return NT_STATUS_NOT_SUPPORTED; +} + +/* + lock a byte range +*/ +static NTSTATUS cvfs_lock(struct request_context *req, union smb_lock *lck) +{ + struct cvfs_private *private = req->conn->ntvfs_private; + struct cli_request *c_req; + + if (!req->async.send_fn) { + return smb_raw_lock(private->tree, lck); + } + + c_req = smb_raw_lock_send(private->tree, lck); + SIMPLE_ASYNC_TAIL; +} + +/* + set info on a open file +*/ +static NTSTATUS cvfs_setfileinfo(struct request_context *req, + union smb_setfileinfo *info) +{ + struct cvfs_private *private = req->conn->ntvfs_private; + struct cli_request *c_req; + + if (!req->async.send_fn) { + return smb_raw_setfileinfo(private->tree, info); + } + c_req = smb_raw_setfileinfo_send(private->tree, info); + + SIMPLE_ASYNC_TAIL; +} + + +/* + a handler for async fsinfo replies + */ +static void async_fsinfo(struct cli_request *c_req) +{ + struct async_info *async = c_req->async.private; + struct request_context *req = async->req; + req->async.status = smb_raw_fsinfo_recv(c_req, req->mem_ctx, async->parms); + req->async.send_fn(req); +} + +/* + return filesystem space info +*/ +static NTSTATUS cvfs_fsinfo(struct request_context *req, union smb_fsinfo *fs) +{ + struct cvfs_private *private = req->conn->ntvfs_private; + struct cli_request *c_req; + + if (!req->async.send_fn) { + return smb_raw_fsinfo(private->tree, req->mem_ctx, fs); + } + + c_req = smb_raw_fsinfo_send(private->tree, req->mem_ctx, fs); + + ASYNC_RECV_TAIL(fs, async_fsinfo); +} + +/* + return print queue info +*/ +static NTSTATUS cvfs_lpq(struct request_context *req, union smb_lpq *lpq) +{ + return NT_STATUS_NOT_SUPPORTED; +} + +/* + list files in a directory matching a wildcard pattern +*/ +static NTSTATUS cvfs_search_first(struct request_context *req, union smb_search_first *io, + void *search_private, + BOOL (*callback)(void *, union smb_search_data *)) +{ + struct cvfs_private *private = req->conn->ntvfs_private; + + return smb_raw_search_first(private->tree, req->mem_ctx, io, search_private, callback); +} + +/* continue a search */ +static NTSTATUS cvfs_search_next(struct request_context *req, union smb_search_next *io, + void *search_private, + BOOL (*callback)(void *, union smb_search_data *)) +{ + struct cvfs_private *private = req->conn->ntvfs_private; + + return smb_raw_search_next(private->tree, req->mem_ctx, io, search_private, callback); +} + +/* close a search */ +static NTSTATUS cvfs_search_close(struct request_context *req, union smb_search_close *io) +{ + struct cvfs_private *private = req->conn->ntvfs_private; + + return smb_raw_search_close(private->tree, io); +} + +/* + a handler for async trans2 replies + */ +static void async_trans2(struct cli_request *c_req) +{ + struct async_info *async = c_req->async.private; + struct request_context *req = async->req; + req->async.status = smb_raw_trans2_recv(c_req, req->mem_ctx, async->parms); + req->async.send_fn(req); +} + +/* raw trans2 */ +static NTSTATUS cvfs_trans2(struct request_context *req, struct smb_trans2 *trans2) +{ + struct cvfs_private *private = req->conn->ntvfs_private; + struct cli_request *c_req; + + if (!req->async.send_fn) { + return smb_raw_trans2(private->tree, req->mem_ctx, trans2); + } + + c_req = smb_raw_trans2_send(private->tree, trans2); + + ASYNC_RECV_TAIL(trans2, async_trans2); +} + +/* + initialise the CIFS->CIFS backend, registering ourselves with the ntvfs subsystem + */ +BOOL cifs_vfs_init(void) +{ + BOOL ret; + struct ntvfs_ops ops; + + ZERO_STRUCT(ops); + + /* fill in all the operations */ + ops.connect = cvfs_connect; + ops.disconnect = cvfs_disconnect; + ops.unlink = cvfs_unlink; + ops.chkpath = cvfs_chkpath; + ops.qpathinfo = cvfs_qpathinfo; + ops.setpathinfo = cvfs_setpathinfo; + ops.open = cvfs_open; + ops.mkdir = cvfs_mkdir; + ops.rmdir = cvfs_rmdir; + ops.rename = cvfs_rename; + ops.copy = cvfs_copy; + ops.ioctl = cvfs_ioctl; + ops.read = cvfs_read; + ops.write = cvfs_write; + ops.seek = cvfs_seek; + ops.flush = cvfs_flush; + ops.close = cvfs_close; + ops.exit = cvfs_exit; + ops.lock = cvfs_lock; + ops.setfileinfo = cvfs_setfileinfo; + ops.qfileinfo = cvfs_qfileinfo; + ops.fsinfo = cvfs_fsinfo; + ops.lpq = cvfs_lpq; + ops.search_first = cvfs_search_first; + ops.search_next = cvfs_search_next; + ops.search_close = cvfs_search_close; + + /* only define this one for trans2 testing */ + ops.trans2 = cvfs_trans2; + + /* register ourselves with the NTVFS subsystem. We register under the name 'cifs'. */ + + ret = ntvfs_register("cifs", NTVFS_DISK, &ops); + + if (!ret) { + DEBUG(0,("Failed to register CIFS backend!\n")); + return False; + } + + return True; +} diff --git a/source4/ntvfs/ipc/README b/source4/ntvfs/ipc/README new file mode 100644 index 0000000000..059a7146e5 --- /dev/null +++ b/source4/ntvfs/ipc/README @@ -0,0 +1,5 @@ +This is the IPC$ backend for Samba. NTVFS operations that are made on +IPC$ shares are directed here by default. Most file operations +are not supported on IPC$ shares. + + diff --git a/source4/ntvfs/ipc/vfs_ipc.c b/source4/ntvfs/ipc/vfs_ipc.c new file mode 100644 index 0000000000..fe310d104e --- /dev/null +++ b/source4/ntvfs/ipc/vfs_ipc.c @@ -0,0 +1,295 @@ +/* + Unix SMB/CIFS implementation. + default IPC$ NTVFS backend + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the 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 implements the IPC$ backend, called by the NTVFS subsystem to + handle requests on IPC$ shares +*/ + + +#include "includes.h" + +/* + connect to a share - always works +*/ +static NTSTATUS ipc_connect(struct request_context *req, const char *sharename) +{ + struct tcon_context *conn = req->conn; + + conn->fs_type = talloc_strdup(conn->mem_ctx, "IPC"); + conn->dev_type = talloc_strdup(conn->mem_ctx, "IPC"); + + return NT_STATUS_OK; +} + +/* + disconnect from a share +*/ +static NTSTATUS ipc_disconnect(struct tcon_context *tcon) +{ + return NT_STATUS_OK; +} + +/* + delete a file +*/ +static NTSTATUS ipc_unlink(struct request_context *req, struct smb_unlink *unl) +{ + return NT_STATUS_ACCESS_DENIED; +} + + +/* + ioctl interface - we don't do any +*/ +static NTSTATUS ipc_ioctl(struct request_context *req, struct smb_ioctl *io) +{ + return NT_STATUS_ACCESS_DENIED; +} + +/* + check if a directory exists +*/ +static NTSTATUS ipc_chkpath(struct request_context *req, struct smb_chkpath *cp) +{ + return NT_STATUS_ACCESS_DENIED; +} + +/* + return info on a pathname +*/ +static NTSTATUS ipc_qpathinfo(struct request_context *req, union smb_fileinfo *info) +{ + return NT_STATUS_ACCESS_DENIED; +} + +/* + set info on a pathname +*/ +static NTSTATUS ipc_setpathinfo(struct request_context *req, union smb_setfileinfo *st) +{ + return NT_STATUS_ACCESS_DENIED; +} + +/* + open a file +*/ +static NTSTATUS ipc_open(struct request_context *req, union smb_open *oi) +{ + return NT_STATUS_ACCESS_DENIED; +} + +/* + create a directory +*/ +static NTSTATUS ipc_mkdir(struct request_context *req, union smb_mkdir *md) +{ + return NT_STATUS_ACCESS_DENIED; +} + +/* + remove a directory +*/ +static NTSTATUS ipc_rmdir(struct request_context *req, struct smb_rmdir *rd) +{ + return NT_STATUS_ACCESS_DENIED; +} + +/* + rename a set of files +*/ +static NTSTATUS ipc_rename(struct request_context *req, struct smb_rename *ren) +{ + return NT_STATUS_ACCESS_DENIED; +} + +/* + copy a set of files +*/ +static NTSTATUS ipc_copy(struct request_context *req, struct smb_copy *cp) +{ + return NT_STATUS_ACCESS_DENIED; +} + +/* + read from a file +*/ +static NTSTATUS ipc_read(struct request_context *req, union smb_read *rd) +{ + return NT_STATUS_ACCESS_DENIED; +} + +/* + write to a file +*/ +static NTSTATUS ipc_write(struct request_context *req, union smb_write *wr) +{ + return NT_STATUS_ACCESS_DENIED; +} + +/* + seek in a file +*/ +static NTSTATUS ipc_seek(struct request_context *req, struct smb_seek *io) +{ + return NT_STATUS_ACCESS_DENIED; +} + +/* + flush a file +*/ +static NTSTATUS ipc_flush(struct request_context *req, struct smb_flush *io) +{ + return NT_STATUS_ACCESS_DENIED; +} + +/* + close a file +*/ +static NTSTATUS ipc_close(struct request_context *req, union smb_close *io) +{ + return NT_STATUS_ACCESS_DENIED; +} + +/* + exit - closing files? +*/ +static NTSTATUS ipc_exit(struct request_context *req) +{ + return NT_STATUS_ACCESS_DENIED; +} + +/* + lock a byte range +*/ +static NTSTATUS ipc_lock(struct request_context *req, union smb_lock *lck) +{ + return NT_STATUS_ACCESS_DENIED; +} + +/* + set info on a open file +*/ +static NTSTATUS ipc_setfileinfo(struct request_context *req, union smb_setfileinfo *info) +{ + return NT_STATUS_ACCESS_DENIED; +} + +/* + query info on a open file +*/ +static NTSTATUS ipc_qfileinfo(struct request_context *req, union smb_fileinfo *info) +{ + return NT_STATUS_ACCESS_DENIED; +} + + +/* + return filesystem info +*/ +static NTSTATUS ipc_fsinfo(struct request_context *req, union smb_fsinfo *fs) +{ + return NT_STATUS_ACCESS_DENIED; +} + +/* + return print queue info +*/ +static NTSTATUS ipc_lpq(struct request_context *req, union smb_lpq *lpq) +{ + return NT_STATUS_ACCESS_DENIED; +} + +/* + list files in a directory matching a wildcard pattern +*/ +NTSTATUS ipc_search_first(struct request_context *req, union smb_search_first *io, + void *search_private, + BOOL (*callback)(void *, union smb_search_data *)) +{ + return NT_STATUS_ACCESS_DENIED; +} + +/* + continue listing files in a directory +*/ +NTSTATUS ipc_search_next(struct request_context *req, union smb_search_next *io, + void *search_private, + BOOL (*callback)(void *, union smb_search_data *)) +{ + return NT_STATUS_ACCESS_DENIED; +} + +/* + end listing files in a directory +*/ +NTSTATUS ipc_search_close(struct request_context *req, union smb_search_close *io) +{ + return NT_STATUS_ACCESS_DENIED; +} + + +/* + initialialise the IPC backend, registering ourselves with the ntvfs subsystem + */ +BOOL ipc_vfs_init(void) +{ + BOOL ret; + struct ntvfs_ops ops; + + ZERO_STRUCT(ops); + + /* fill in all the operations */ + ops.connect = ipc_connect; + ops.disconnect = ipc_disconnect; + ops.unlink = ipc_unlink; + ops.chkpath = ipc_chkpath; + ops.qpathinfo = ipc_qpathinfo; + ops.setpathinfo = ipc_setpathinfo; + ops.open = ipc_open; + ops.mkdir = ipc_mkdir; + ops.rmdir = ipc_rmdir; + ops.rename = ipc_rename; + ops.copy = ipc_copy; + ops.ioctl = ipc_ioctl; + ops.read = ipc_read; + ops.write = ipc_write; + ops.seek = ipc_seek; + ops.flush = ipc_flush; + ops.close = ipc_close; + ops.exit = ipc_exit; + ops.lock = ipc_lock; + ops.setfileinfo = ipc_setfileinfo; + ops.qfileinfo = ipc_qfileinfo; + ops.fsinfo = ipc_fsinfo; + ops.lpq = ipc_lpq; + ops.search_first = ipc_search_first; + ops.search_next = ipc_search_next; + ops.search_close = ipc_search_close; + + /* register ourselves with the NTVFS subsystem. */ + ret = ntvfs_register("ipc", NTVFS_IPC, &ops); + + if (!ret) { + DEBUG(0,("Failed to register IPC backend!\n")); + return False; + } + + return True; +} diff --git a/source4/ntvfs/ntvfs_base.c b/source4/ntvfs/ntvfs_base.c new file mode 100644 index 0000000000..57cdb97e9f --- /dev/null +++ b/source4/ntvfs/ntvfs_base.c @@ -0,0 +1,145 @@ +/* + Unix SMB/CIFS implementation. + NTVFS base code + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the 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 implements the core code for all NTVFS modules. Backends register themselves here. +*/ + +#include "includes.h" + + +/* the list of currently registered NTVFS backends, note that there + * can be more than one backend with the same name, as long as they + * have different typesx */ +static struct { + const char *name; + enum ntvfs_type type; + struct ntvfs_ops *ops; +} *backends = NULL; +static int num_backends; + +/* + register a NTVFS backend. + + The 'name' can be later used by other backends to find the operations + structure for this backend. + + The 'type' is used to specify whether this is for a disk, printer or IPC$ share +*/ +BOOL ntvfs_register(const char *name, enum ntvfs_type type, struct ntvfs_ops *ops) +{ + if (ntvfs_backend_byname(name, type) != NULL) { + /* its already registered! */ + DEBUG(2,("NTVFS backend '%s' for type %d already registered\n", + name, (int)type)); + return False; + } + + backends = Realloc(backends, sizeof(backends[0]) * (num_backends+1)); + if (!backends) { + smb_panic("out of memory in ntvfs_register"); + } + + backends[num_backends].name = smb_xstrdup(name); + backends[num_backends].type = type; + backends[num_backends].ops = smb_xmemdup(ops, sizeof(*ops)); + + num_backends++; + + return True; +} + + +/* + return the operations structure for a named backend of the specified type +*/ +struct ntvfs_ops *ntvfs_backend_byname(const char *name, enum ntvfs_type type) +{ + int i; + + for (i=0;isizeof_ntvfs_ops = sizeof(struct ntvfs_ops); + sizes->sizeof_SMB_OFF_T = sizeof(SMB_OFF_T); + sizes->sizeof_tcon_context = sizeof(struct tcon_context); + sizes->sizeof_request_context = sizeof(struct request_context); + + return NTVFS_INTERFACE_VERSION; +} + + +/* + initialise the NTVFS subsystem +*/ +BOOL ntvfs_init(void) +{ + /* initialise our 3 basic backends. These are assumed to be + * present and are always built in */ + if (!posix_vfs_init() || + !ipc_vfs_init() || + !print_vfs_init()) { + return False; + } + /* initialize optional backends, e.g. CIFS. We allow failures here. */ + cifs_vfs_init(); + +#if WITH_NTVFS_STFS + tank_vfs_init(); +#endif + + DEBUG(3,("NTVFS version %d initialised\n", NTVFS_INTERFACE_VERSION)); + return True; +} + + +/* + initialise a connection structure to point at a NTVFS backend +*/ +NTSTATUS ntvfs_init_connection(struct request_context *req) +{ + const char *handler = lp_ntvfs_handler(req->conn->service); + + if (strequal(handler, "default")) + handler = "ipc"; + + req->conn->ntvfs_ops = ntvfs_backend_byname(handler, req->conn->type); + + if (!req->conn->ntvfs_ops) { + DEBUG(1,("ntvfs_init_connection: failed to find backend=%s, type=%d\n", handler, req->conn->type)); + return NT_STATUS_UNSUCCESSFUL; + } + + return NT_STATUS_OK; +} diff --git a/source4/ntvfs/ntvfs_dfs.c b/source4/ntvfs/ntvfs_dfs.c new file mode 100644 index 0000000000..7acd1f7cbb --- /dev/null +++ b/source4/ntvfs/ntvfs_dfs.c @@ -0,0 +1,117 @@ +/* + Unix SMB/CIFS implementation. + NTVFS base code + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the 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 implements the core code for all NTVFS modules. Backends register themselves here. +*/ + +#include "includes.h" + + +/* the list of currently registered NTVFS backends, note that there + * can be more than one backend with the same name, as long as they + * have different typesx */ +static struct { + const char *name; + enum ntvfs_type type; + struct ntvfs_ops *ops; +} *backends = NULL; +static int num_backends; + +/* + register a NTVFS backend. + + The 'name' can be later used by other backends to find the operations + structure for this backend. + + The 'type' is used to specify whether this is for a disk, printer or IPC$ share +*/ +BOOL ntvfs_register(const char *name, enum ntvfs_type type, struct ntvfs_ops *ops) +{ + if (ntvfs_backend_byname(name, type) != NULL) { + /* its already registered! */ + DEBUG(2,("NTVFS backend '%s' for type %d already registered\n", + name, (int)type)); + return False; + } + + backends = Realloc(backends, sizeof(backends[0]) * (num_backends+1)); + if (!backends) { + smb_panic("out of memory in ntvfs_register"); + } + + backends[num_backends].name = smb_xstrdup(name); + backends[num_backends].type = type; + backends[num_backends].ops = smb_xmemdup(ops, sizeof(*ops)); + + num_backends++; + + return True; +} + + +/* + return the operations structure for a named backend of the specified type +*/ +struct ntvfs_ops *ntvfs_backend_byname(const char *name, enum ntvfs_type type) +{ + int i; + + for (i=0;isizeof_ntvfs_ops = sizeof(struct ntvfs_ops); + sizes->sizeof_SMB_OFF_T = sizeof(SMB_OFF_T); + sizes->sizeof_tcon_context = sizeof(struct tcon_context); + + return NTVFS_INTERFACE_VERSION; +} + + +/* + initialise the NTVFS subsystem +*/ +BOOL ntvfs_init(void) +{ + /* initialise our 3 basic backends. These are assumed to be + * present and are always built in */ + if (!posix_vfs_init() || + !ipc_vfs_init() || + !print_vfs_init()) { + return False; + } + + DEBUG(3,("NTVFS version %d initialised\n", NTVFS_INTERFACE_VERSION)); + return True; +} diff --git a/source4/ntvfs/ntvfs_generic.c b/source4/ntvfs/ntvfs_generic.c new file mode 100644 index 0000000000..def448a671 --- /dev/null +++ b/source4/ntvfs/ntvfs_generic.c @@ -0,0 +1,544 @@ +/* + Unix SMB/CIFS implementation. + + NTVFS generic level mapping code + + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the 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 implements mappings between info levels for NTVFS backend calls + + the idea is that each of these functions implements one of the NTVFS + backend calls in terms of the 'generic' call. All backends that use + these functions must supply the generic call, but can if it wants to + also implement other levels if the need arises + + this allows backend writers to only implement one varient of each + call unless they need fine grained control of the calls. +*/ + +#include "includes.h" + +/* + see if a filename ends in EXE COM DLL or SYM. This is needed for the DENY_DOS mapping for OpenX +*/ +static BOOL is_exe_file(const char *fname) +{ + char *p; + p = strrchr(fname, '.'); + if (!p) { + return False; + } + p++; + if (strcasecmp(p, "EXE") == 0 || + strcasecmp(p, "COM") == 0 || + strcasecmp(p, "DLL") == 0 || + strcasecmp(p, "SYM") == 0) { + return True; + } + return False; +} + + +/* + NTVFS open generic to any mapper +*/ +NTSTATUS ntvfs_map_open(struct request_context *req, union smb_open *io) +{ + NTSTATUS status; + union smb_open io2; + + if (io->generic.level == RAW_OPEN_GENERIC) { + return NT_STATUS_INVALID_LEVEL; + } + + switch (io->generic.level) { + case RAW_OPEN_OPENX: + ZERO_STRUCT(io2.generic.in); + io2.generic.level = RAW_OPEN_GENERIC; + if (io->openx.in.flags & OPENX_FLAGS_REQUEST_OPLOCK) { + io2.generic.in.flags |= NTCREATEX_FLAGS_REQUEST_OPLOCK; + } + if (io->openx.in.flags & OPENX_FLAGS_REQUEST_BATCH_OPLOCK) { + io2.generic.in.flags |= NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + } + + switch (io->openx.in.open_mode & OPENX_MODE_ACCESS_MASK) { + case OPENX_MODE_ACCESS_READ: + io2.generic.in.access_mask = GENERIC_RIGHTS_FILE_READ; + break; + case OPENX_MODE_ACCESS_WRITE: + io2.generic.in.access_mask = GENERIC_RIGHTS_FILE_WRITE; + break; + case OPENX_MODE_ACCESS_RDWR: + case OPENX_MODE_ACCESS_FCB: + io2.generic.in.access_mask = GENERIC_RIGHTS_FILE_ALL_ACCESS; + break; + } + + switch (io->openx.in.open_mode & OPENX_MODE_DENY_MASK) { + case OPENX_MODE_DENY_READ: + io2.generic.in.share_access = NTCREATEX_SHARE_ACCESS_WRITE; + break; + case OPENX_MODE_DENY_WRITE: + io2.generic.in.share_access = NTCREATEX_SHARE_ACCESS_READ; + break; + case OPENX_MODE_DENY_ALL: + io2.generic.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + break; + case OPENX_MODE_DENY_NONE: + io2.generic.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + break; + case OPENX_MODE_DENY_DOS: + /* DENY_DOS is quite strange - it depends on the filename! */ + if (is_exe_file(io->openx.in.fname)) { + io2.generic.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + } else { + if ((io->openx.in.open_mode & OPENX_MODE_ACCESS_MASK) == + OPENX_MODE_ACCESS_READ) { + io2.generic.in.share_access = NTCREATEX_SHARE_ACCESS_READ; + } else { + io2.generic.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + } + } + break; + case OPENX_MODE_DENY_FCB: + io2.generic.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + break; + } + + switch (io->openx.in.open_func) { + case (OPENX_OPEN_FUNC_FAIL): + io2.generic.in.open_disposition = NTCREATEX_DISP_CREATE; + break; + case (OPENX_OPEN_FUNC_OPEN): + io2.generic.in.open_disposition = NTCREATEX_DISP_OPEN; + break; + case (OPENX_OPEN_FUNC_TRUNC): + io2.generic.in.open_disposition = NTCREATEX_DISP_OVERWRITE; + break; + case (OPENX_OPEN_FUNC_FAIL | OPENX_OPEN_FUNC_CREATE): + io2.generic.in.open_disposition = NTCREATEX_DISP_CREATE; + break; + case (OPENX_OPEN_FUNC_OPEN | OPENX_OPEN_FUNC_CREATE): + io2.generic.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + break; + case (OPENX_OPEN_FUNC_TRUNC | OPENX_OPEN_FUNC_CREATE): + io2.generic.in.open_disposition = NTCREATEX_DISP_OVERWRITE_IF; + break; + } + io2.generic.in.alloc_size = io->openx.in.size; + io2.generic.in.file_attr = io->openx.in.file_attrs; + io2.generic.in.fname = io->openx.in.fname; + + status = req->conn->ntvfs_ops->open(req, &io2); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + ZERO_STRUCT(io->openx.out); + io->openx.out.fnum = io2.generic.out.fnum; + io->openx.out.attrib = io2.generic.out.attrib; + io->openx.out.write_time = nt_time_to_unix(&io2.generic.out.write_time); + io->openx.out.size = io2.generic.out.size; + + return NT_STATUS_OK; + + + case RAW_OPEN_OPEN: + ZERO_STRUCT(io2.generic.in); + io2.generic.level = RAW_OPEN_GENERIC; + io2.generic.in.file_attr = io->open.in.search_attrs; + io2.generic.in.fname = io->open.in.fname; + io2.generic.in.open_disposition = NTCREATEX_DISP_OPEN; + DEBUG(9,("ntvfs_map_open(OPEN): mapping flags=0x%x\n", + io->open.in.flags)); + switch (io->open.in.flags & OPEN_FLAGS_MODE_MASK) { + case OPEN_FLAGS_OPEN_READ: + io2.generic.in.access_mask = GENERIC_RIGHTS_FILE_READ; + io->open.out.rmode = DOS_OPEN_RDONLY; + break; + case OPEN_FLAGS_OPEN_WRITE: + io2.generic.in.access_mask = GENERIC_RIGHTS_FILE_WRITE; + io->open.out.rmode = DOS_OPEN_WRONLY; + break; + case OPEN_FLAGS_OPEN_RDWR: + case 0xf: /* FCB mode */ + io2.generic.in.access_mask = GENERIC_RIGHTS_FILE_ALL_ACCESS; + io->open.out.rmode = DOS_OPEN_RDWR; /* assume we got r/w */ + break; + default: + DEBUG(2,("ntvfs_map_open(OPEN): invalid mode 0x%x\n", + io->open.in.flags & OPEN_FLAGS_MODE_MASK)); + return NT_STATUS_INVALID_PARAMETER; + } + + switch(io->open.in.flags & OPEN_FLAGS_DENY_MASK) { + case OPEN_FLAGS_DENY_DOS: + /* DENY_DOS is quite strange - it depends on the filename! */ + /* REWRITE: is this necessary for OPEN? */ + if (is_exe_file(io->open.in.fname)) { + io2.generic.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + } else { + if ((io->open.in.flags & OPEN_FLAGS_MODE_MASK) == + OPEN_FLAGS_OPEN_READ) { + io2.generic.in.share_access = NTCREATEX_SHARE_ACCESS_READ; + } else { + io2.generic.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + } + } + break; + case OPEN_FLAGS_DENY_ALL: + io2.generic.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + break; + case OPEN_FLAGS_DENY_WRITE: + io2.generic.in.share_access = NTCREATEX_SHARE_ACCESS_READ; + break; + case OPEN_FLAGS_DENY_READ: + io2.generic.in.share_access = NTCREATEX_SHARE_ACCESS_WRITE; + break; + case OPEN_FLAGS_DENY_NONE: + io2.generic.in.share_access = NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_DELETE; + break; + case 0x70: /* FCB mode */ + io2.generic.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + break; + default: + DEBUG(2,("ntvfs_map_open(OPEN): invalid DENY 0x%x\n", + io->open.in.flags & OPEN_FLAGS_DENY_MASK)); + return NT_STATUS_INVALID_PARAMETER; + } + DEBUG(9,("ntvfs_map_open(OPEN): mapped flags=0x%x to access_mask=0x%x and share_access=0x%x\n", + io->open.in.flags, io2.generic.in.access_mask, io2.generic.in.share_access)); + + status = req->conn->ntvfs_ops->open(req, &io2); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + ZERO_STRUCT(io->openx.out); + io->open.out.fnum = io2.generic.out.fnum; + io->open.out.attrib = io2.generic.out.attrib; + io->open.out.write_time = nt_time_to_unix(&io2.generic.out.write_time); + io->open.out.size = io2.generic.out.size; + io->open.out.rmode = DOS_OPEN_RDWR; + + return NT_STATUS_OK; + } + + return NT_STATUS_INVALID_LEVEL; +} + + +/* + NTVFS fsinfo generic to any mapper +*/ +NTSTATUS ntvfs_map_fsinfo(struct request_context *req, union smb_fsinfo *fs) +{ + NTSTATUS status; + union smb_fsinfo fs2; + + if (fs->generic.level == RAW_QFS_GENERIC) { + return NT_STATUS_INVALID_LEVEL; + } + + /* ask the backend for the generic info */ + fs2.generic.level = RAW_QFS_GENERIC; + + status = req->conn->ntvfs_ops->fsinfo(req, &fs2); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* and convert it to the required level */ + switch (fs->generic.level) { + case RAW_QFS_GENERIC: + return NT_STATUS_INVALID_LEVEL; + + case RAW_QFS_DSKATTR: { + /* map from generic to DSKATTR */ + unsigned bpunit = 64; + + /* we need to scale the sizes to fit */ + for (bpunit=64; bpunit<0x10000; bpunit *= 2) { + if (fs2.generic.out.blocks_total * (double)fs2.generic.out.block_size < bpunit * 512 * 65535.0) { + break; + } + } + + fs->dskattr.out.blocks_per_unit = bpunit; + fs->dskattr.out.block_size = 512; + fs->dskattr.out.units_total = + (fs2.generic.out.blocks_total * (double)fs2.generic.out.block_size) / (bpunit * 512); + fs->dskattr.out.units_free = + (fs2.generic.out.blocks_free * (double)fs2.generic.out.block_size) / (bpunit * 512); + + /* we must return a maximum of 2G to old DOS systems, or they get very confused */ + if (bpunit > 64 && req->smb->negotiate.protocol <= PROTOCOL_LANMAN2) { + fs->dskattr.out.blocks_per_unit = 64; + fs->dskattr.out.units_total = 0xFFFF; + fs->dskattr.out.units_free = 0xFFFF; + } + return NT_STATUS_OK; + } + + case RAW_QFS_ALLOCATION: + fs->allocation.out.fs_id = fs2.generic.out.fs_id; + fs->allocation.out.total_alloc_units = fs2.generic.out.blocks_total; + fs->allocation.out.avail_alloc_units = fs2.generic.out.blocks_free; + fs->allocation.out.sectors_per_unit = 1; + fs->allocation.out.bytes_per_sector = fs2.generic.out.block_size; + return NT_STATUS_OK; + + case RAW_QFS_VOLUME: + fs->volume.out.serial_number = fs2.generic.out.serial_number; + fs->volume.out.volume_name.s = fs2.generic.out.volume_name; + return NT_STATUS_OK; + + case RAW_QFS_VOLUME_INFO: + case RAW_QFS_VOLUME_INFORMATION: + fs->volume_info.out.create_time = fs2.generic.out.create_time; + fs->volume_info.out.serial_number = fs2.generic.out.serial_number; + fs->volume_info.out.volume_name.s = fs2.generic.out.volume_name; + return NT_STATUS_OK; + + case RAW_QFS_SIZE_INFO: + case RAW_QFS_SIZE_INFORMATION: + fs->size_info.out.total_alloc_units = fs2.generic.out.blocks_total; + fs->size_info.out.avail_alloc_units = fs2.generic.out.blocks_free; + fs->size_info.out.sectors_per_unit = 1; + fs->size_info.out.bytes_per_sector = fs2.generic.out.block_size; + return NT_STATUS_OK; + + case RAW_QFS_DEVICE_INFO: + case RAW_QFS_DEVICE_INFORMATION: + fs->device_info.out.device_type = fs2.generic.out.device_type; + fs->device_info.out.characteristics = fs2.generic.out.device_characteristics; + return NT_STATUS_OK; + + case RAW_QFS_ATTRIBUTE_INFO: + case RAW_QFS_ATTRIBUTE_INFORMATION: + fs->attribute_info.out.fs_attr = fs2.generic.out.fs_attr; + fs->attribute_info.out.max_file_component_length = fs2.generic.out.max_file_component_length; + fs->attribute_info.out.fs_type.s = fs2.generic.out.fs_type; + return NT_STATUS_OK; + + case RAW_QFS_QUOTA_INFORMATION: + ZERO_STRUCT(fs->quota_information.out.unknown); + fs->quota_information.out.quota_soft = fs2.generic.out.quota_soft; + fs->quota_information.out.quota_hard = fs2.generic.out.quota_hard; + fs->quota_information.out.quota_flags = fs2.generic.out.quota_flags; + return NT_STATUS_OK; + + case RAW_QFS_FULL_SIZE_INFORMATION: + fs->full_size_information.out.total_alloc_units = fs2.generic.out.blocks_total; + fs->full_size_information.out.call_avail_alloc_units = fs2.generic.out.blocks_free; + fs->full_size_information.out.actual_avail_alloc_units = fs2.generic.out.blocks_free; + fs->full_size_information.out.sectors_per_unit = 1; + fs->full_size_information.out.bytes_per_sector = fs2.generic.out.block_size; + return NT_STATUS_OK; + + case RAW_QFS_OBJECTID_INFORMATION: + fs->objectid_information.out.guid = fs2.generic.out.guid; + ZERO_STRUCT(fs->objectid_information.out.unknown); + return NT_STATUS_OK; + } + + + return NT_STATUS_INVALID_LEVEL; +} + + +/* + NTVFS fileinfo generic to any mapper +*/ +NTSTATUS ntvfs_map_fileinfo(struct request_context *req, union smb_fileinfo *info, union smb_fileinfo *info2) +{ + /* and convert it to the required level using results in info2 */ + switch (info->generic.level) { + case RAW_FILEINFO_GENERIC: + return NT_STATUS_INVALID_LEVEL; + case RAW_FILEINFO_GETATTR: + info->getattr.out.attrib = info2->generic.out.attrib & 0x3f; + info->getattr.out.size = info2->generic.out.size; + info->getattr.out.write_time = nt_time_to_unix(&info2->generic.out.write_time); + return NT_STATUS_OK; + + case RAW_FILEINFO_GETATTRE: + info->getattre.out.attrib = info2->generic.out.attrib; + info->getattre.out.size = info2->generic.out.size; + info->getattre.out.write_time = nt_time_to_unix(&info2->generic.out.write_time); + info->getattre.out.create_time = nt_time_to_unix(&info2->generic.out.create_time); + info->getattre.out.access_time = nt_time_to_unix(&info2->generic.out.access_time); + info->getattre.out.alloc_size = info2->generic.out.alloc_size; + return NT_STATUS_OK; + + case RAW_FILEINFO_NETWORK_OPEN_INFORMATION: + info->network_open_information.out.create_time = info2->generic.out.create_time; + info->network_open_information.out.access_time = info2->generic.out.access_time; + info->network_open_information.out.write_time = info2->generic.out.write_time; + info->network_open_information.out.change_time = info2->generic.out.change_time; + info->network_open_information.out.alloc_size = info2->generic.out.alloc_size; + info->network_open_information.out.size = info2->generic.out.size; + info->network_open_information.out.attrib = info2->generic.out.attrib; + return NT_STATUS_OK; + + case RAW_FILEINFO_ALL_INFO: + case RAW_FILEINFO_ALL_INFORMATION: + info->all_info.out.create_time = info2->generic.out.create_time; + info->all_info.out.access_time = info2->generic.out.access_time; + info->all_info.out.write_time = info2->generic.out.write_time; + info->all_info.out.change_time = info2->generic.out.change_time; + info->all_info.out.attrib = info2->generic.out.attrib; + info->all_info.out.alloc_size = info2->generic.out.alloc_size; + info->all_info.out.size = info2->generic.out.size; + info->all_info.out.nlink = info2->generic.out.nlink; + info->all_info.out.delete_pending = info2->generic.out.delete_pending; + info->all_info.out.directory = info2->generic.out.directory; + info->all_info.out.ea_size = info2->generic.out.ea_size; + info->all_info.out.fname.s = info2->generic.out.fname.s; + info->all_info.out.fname.private_length = info2->generic.out.fname.private_length; + return NT_STATUS_OK; + + case RAW_FILEINFO_BASIC_INFO: + case RAW_FILEINFO_BASIC_INFORMATION: + info->basic_info.out.create_time = info2->generic.out.create_time; + info->basic_info.out.access_time = info2->generic.out.access_time; + info->basic_info.out.write_time = info2->generic.out.write_time; + info->basic_info.out.change_time = info2->generic.out.change_time; + info->basic_info.out.attrib = info2->generic.out.attrib; + return NT_STATUS_OK; + + case RAW_FILEINFO_STANDARD: + info->standard.out.create_time = nt_time_to_unix(&info2->generic.out.create_time); + info->standard.out.access_time = nt_time_to_unix(&info2->generic.out.access_time); + info->standard.out.write_time = nt_time_to_unix(&info2->generic.out.write_time); + info->standard.out.size = info2->generic.out.size; + info->standard.out.alloc_size = info2->generic.out.alloc_size; + info->standard.out.attrib = info2->generic.out.attrib; + return NT_STATUS_OK; + + case RAW_FILEINFO_EA_SIZE: + info->ea_size.out.create_time = nt_time_to_unix(&info2->generic.out.create_time); + info->ea_size.out.access_time = nt_time_to_unix(&info2->generic.out.access_time); + info->ea_size.out.write_time = nt_time_to_unix(&info2->generic.out.write_time); + info->ea_size.out.size = info2->generic.out.size; + info->ea_size.out.alloc_size = info2->generic.out.alloc_size; + info->ea_size.out.attrib = info2->generic.out.attrib; + info->ea_size.out.ea_size = info2->generic.out.ea_size; + return NT_STATUS_OK; + + case RAW_FILEINFO_STANDARD_INFO: + case RAW_FILEINFO_STANDARD_INFORMATION: + info->standard_info.out.alloc_size = info2->generic.out.alloc_size; + info->standard_info.out.size = info2->generic.out.size; + info->standard_info.out.nlink = info2->generic.out.nlink; + info->standard_info.out.delete_pending = info2->generic.out.delete_pending; + info->standard_info.out.directory = info2->generic.out.directory; + return NT_STATUS_OK; + + case RAW_FILEINFO_INTERNAL_INFORMATION: + info->internal_information.out.device = info2->generic.out.device; + info->internal_information.out.inode = info2->generic.out.inode; + return NT_STATUS_OK; + + case RAW_FILEINFO_EA_INFO: + case RAW_FILEINFO_EA_INFORMATION: + info->ea_info.out.ea_size = info2->generic.out.ea_size; + return NT_STATUS_OK; + + case RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION: + info->attribute_tag_information.out.attrib = info2->generic.out.attrib; + info->attribute_tag_information.out.reparse_tag = info2->generic.out.reparse_tag; + return NT_STATUS_OK; + + case RAW_FILEINFO_STREAM_INFO: + case RAW_FILEINFO_STREAM_INFORMATION: + /* setup a single data stream */ + info->stream_info.out.num_streams = info2->generic.out.num_streams; + info->stream_info.out.streams = talloc(req->mem_ctx, sizeof(info2->stream_info.out.streams[0])); + if (!info->stream_info.out.streams) { + return NT_STATUS_NO_MEMORY; + } + info->stream_info.out.streams[0].size = info2->generic.out.streams[0].size; + info->stream_info.out.streams[0].alloc_size = info2->generic.out.streams[0].alloc_size; + info->stream_info.out.streams[0].stream_name.s = info2->generic.out.streams[0].stream_name.s; + info->stream_info.out.streams[0].stream_name.private_length = info->generic.out.streams[0].stream_name.private_length; + return NT_STATUS_OK; + + case RAW_FILEINFO_NAME_INFO: + case RAW_FILEINFO_NAME_INFORMATION: + info->name_info.out.fname.s = info2->generic.out.fname.s; + info->name_info.out.fname.private_length = info2->generic.out.fname.private_length; + return NT_STATUS_OK; + + case RAW_FILEINFO_ALT_NAME_INFO: + case RAW_FILEINFO_ALT_NAME_INFORMATION: + info->alt_name_info.out.fname.s = info2->generic.out.alt_fname.s; + info->alt_name_info.out.fname.private_length = info2->generic.out.alt_fname.private_length; + return NT_STATUS_OK; + } + + return NT_STATUS_INVALID_LEVEL; +} + +/* + NTVFS fileinfo generic to any mapper +*/ +NTSTATUS ntvfs_map_qfileinfo(struct request_context *req, union smb_fileinfo *info) +{ + NTSTATUS status; + union smb_fileinfo info2; + + if (info->generic.level == RAW_FILEINFO_GENERIC) { + return NT_STATUS_INVALID_LEVEL; + } + + /* ask the backend for the generic info */ + info2.generic.level = RAW_FILEINFO_GENERIC; + info2.generic.in.fnum = info->generic.in.fnum; + + status = req->conn->ntvfs_ops->qfileinfo(req, &info2); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + return ntvfs_map_fileinfo(req, info, &info2); +} + +/* + NTVFS pathinfo generic to any mapper +*/ +NTSTATUS ntvfs_map_qpathinfo(struct request_context *req, union smb_fileinfo *info) +{ + NTSTATUS status; + union smb_fileinfo info2; + + if (info->generic.level == RAW_FILEINFO_GENERIC) { + return NT_STATUS_INVALID_LEVEL; + } + + /* ask the backend for the generic info */ + info2.generic.level = RAW_FILEINFO_GENERIC; + info2.generic.in.fname = info->generic.in.fname; + + status = req->conn->ntvfs_ops->qpathinfo(req, &info2); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + return ntvfs_map_fileinfo(req, info, &info2); +} diff --git a/source4/ntvfs/ntvfs_util.c b/source4/ntvfs/ntvfs_util.c new file mode 100644 index 0000000000..4036fb0935 --- /dev/null +++ b/source4/ntvfs/ntvfs_util.c @@ -0,0 +1,26 @@ +/* + Unix SMB/CIFS implementation. + NTVFS utility code + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the 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 implements common utility functions that many NTVFS backends may wish to use +*/ + +#include "includes.h" + + diff --git a/source4/ntvfs/posix/vfs_posix.c b/source4/ntvfs/posix/vfs_posix.c new file mode 100644 index 0000000000..b27e7493a0 --- /dev/null +++ b/source4/ntvfs/posix/vfs_posix.c @@ -0,0 +1,151 @@ +/* + Unix SMB/CIFS implementation. + POSIX NTVFS backend + Copyright (C) Andrew Tridgell 1992-2003 + 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 implements most of the POSIX NTVFS backend + This is the default backend +*/ + +#include "includes.h" + +/* + connect to a share - used when a tree_connect operation comes + in. For a disk based backend we needs to ensure that the base + directory exists (tho it doesn't need to be accessible by the user, + that comes later) +*/ +static NTSTATUS pvfs_connect(struct ntvfs_context *ctx, const char *sharename) +{ + struct stat st; + struct connection_struct *conn = ctx->conn; + NTSTATUS status; + + /* the directory must exist */ + if (stat(conn->connectpath, &st) != 0 || !S_ISDIR(st.st_mode)) { + DEBUG(0,("'%s' is not a directory, when connecting to [%s]\n", + conn->connectpath, lp_servicename(SNUM(conn)))); + return NT_STATUS_BAD_NETWORK_NAME; + } + + /* Initialise old VFS function pointers */ + if (!smbd_vfs_init(conn)) { + DEBUG(0, ("vfs_init failed for service %s\n", lp_servicename(SNUM(conn)))); + return NT_STATUS_BAD_NETWORK_NAME; + } + + /* become the user for the rest */ + status = ntvfs_change_to_user(ctx); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* the posix backend can do preexec */ + status = ntvfs_connect_preexec(ctx); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* Invoke the old POSIX VFS make connection hook */ + if (conn->vfs_ops.connect && + conn->vfs_ops.connect(conn, lp_servicename(snum), user) < 0) { + DEBUG(0,("make_connection: POSIX VFS make connection failed!\n")); + return NT_STATUS_UNSUCCESSFUL; + } + } + + + /* + * 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( "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() ); + } + + return NT_STATUS_OK; +} + +/* + disconnect from a share +*/ +static NTSTATUS pvfs_disconnect(struct ntvfs_context *ctx) +{ + return NT_STATUS_OK; +} + +/* + delete a file - the dirtype specifies the file types to include in the search. + The name can contain CIFS wildcards, but rarely does (except with OS/2 clients) +*/ +static NTSTATUS pvfs_unlink(struct ntvfs_context *ctx, const char *name, uint16 dirtype) +{ + NTSTATUS status; + + if (ntvfs_dfs_redirect(ctx, name)) { + return NT_STATUS_PATH_NOT_COVERED; + } + + status = unlink_internals(ctx->conn, dirtype, name); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + ntvfs_run_change_notify_queue(); + + return NT_STATUS_OK; +} + + + + + + + +/* + initialialise the POSIX disk backend, registering ourselves with the ntvfs subsystem + */ +BOOL posix_vfs_init(void) +{ + BOOL ret; + struct ntvfs_ops ops; + + ZERO_STRUCT(ops); + + /* fill in all the operations */ + ops.connect = pvfs_connect; + ops.disconnect = pvfs_disconnect; + ops.unlink = pvfs_unlink; + + /* register ourselves with the NTVFS subsystem. We register under the name 'default' + as we wish to be the default backend */ + ret = ntvfs_register("default", NTVFS_DISK, &ops); + + if (!ret) { + DEBUG(0,("Failed to register POSIX backend!\n")); + return False; + } + + return True; +} diff --git a/source4/ntvfs/print/README b/source4/ntvfs/print/README new file mode 100644 index 0000000000..441c82dddd --- /dev/null +++ b/source4/ntvfs/print/README @@ -0,0 +1,3 @@ +This is the print NTVFS backend for Samba. NTVFS operations that are +made on print shares are directed here by default. Most directory +operations and many file operations are not supported on print shares. diff --git a/source4/ntvfs/print/vfs_print.c b/source4/ntvfs/print/vfs_print.c new file mode 100644 index 0000000000..2563f0ad9c --- /dev/null +++ b/source4/ntvfs/print/vfs_print.c @@ -0,0 +1,104 @@ +/* + Unix SMB/CIFS implementation. + default print NTVFS backend + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the 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 implements the print backend, called by the NTVFS subsystem to + handle requests on printing shares +*/ + +#include "includes.h" + +/* + connect to a share - used when a tree_connect operation comes + in. For printing shares this should check that the spool directory + is available +*/ +static NTSTATUS print_connect(struct request_context *req, const char *sharename) +{ + return NT_STATUS_OK; +} + +/* + disconnect from a share +*/ +static NTSTATUS print_disconnect(struct tcon_context *conn) +{ + return NT_STATUS_OK; +} + +/* + lots of operations are not allowed on printing shares - mostly return NT_STATUS_ACCESS_DENIED +*/ +static NTSTATUS print_unlink(struct request_context *req, struct smb_unlink *unl) +{ + return NT_STATUS_ACCESS_DENIED; +} + + +/* + ioctl - used for job query +*/ +static NTSTATUS print_ioctl(struct request_context *req, struct smb_ioctl *io) +{ + char *p; + + if (io->in.request == IOCTL_QUERY_JOB_INFO) { + /* a request for the print job id of an open print job */ + io->out.blob = data_blob_talloc(req->mem_ctx, NULL, 32); + + memset(io->out.blob.data, 0, io->out.blob.length); + + p = io->out.blob.data; + SSVAL(p,0, 1 /* REWRITE: fsp->rap_print_jobid */); + push_string(NULL, p+2, lp_netbios_name(), 15, STR_TERMINATE|STR_ASCII); + push_string(NULL, p+18, lp_servicename(req->conn->service), 13, STR_TERMINATE|STR_ASCII); + return NT_STATUS_OK; + } + + return NT_STATUS_INVALID_PARAMETER; +} + + +/* + initialialise the print backend, registering ourselves with the ntvfs subsystem + */ +BOOL print_vfs_init(void) +{ + BOOL ret; + struct ntvfs_ops ops; + + ZERO_STRUCT(ops); + + /* fill in all the operations */ + ops.connect = print_connect; + ops.disconnect = print_disconnect; + ops.unlink = print_unlink; + ops.ioctl = print_ioctl; + + /* register ourselves with the NTVFS subsystem. We register under the name 'default' + as we wish to be the default backend */ + ret = ntvfs_register("default", NTVFS_PRINT, &ops); + + if (!ret) { + DEBUG(0,("Failed to register PRINT backend!\n")); + return False; + } + + return True; +} diff --git a/source4/ntvfs/reference/ref.h b/source4/ntvfs/reference/ref.h new file mode 100644 index 0000000000..00eab76094 --- /dev/null +++ b/source4/ntvfs/reference/ref.h @@ -0,0 +1,36 @@ + +struct rvfs_private { + /* the meta-data database */ + TDB_CONTEXT *tdb; + + /* the base directory */ + char *connectpath; + + /* a linked list of open searches */ + struct search_state *search; + + /* next available search handle */ + uint16 next_search_handle; +}; + +struct rvfs_dir { + uint_t count; + char *unix_dir; + struct { + char *name; + } *files; +}; + +struct search_state { + struct search_state *next, *prev; + TALLOC_CTX *mem_ctx; + uint16 handle; + uint_t current_index; + struct rvfs_dir *dir; +}; + + +struct ref_struct { + NTTIME mtime, ctime, atime, wtime; + char *; +}; diff --git a/source4/ntvfs/reference/ref_util.c b/source4/ntvfs/reference/ref_util.c new file mode 100644 index 0000000000..8234944ebd --- /dev/null +++ b/source4/ntvfs/reference/ref_util.c @@ -0,0 +1,142 @@ +/* + Unix SMB/CIFS implementation. + + simple NTVFS filesystem backend + + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +/* + utility functions for simple backend +*/ + +#include "includes.h" +#include "svfs.h" + +/* + convert a windows path to a unix path - don't do any manging or case sensitive handling +*/ +char *svfs_unix_path(struct request_context *req, const char *name) +{ + struct svfs_private *private = req->conn->ntvfs_private; + char *ret; + + if (*name != '\\') { + ret = talloc_asprintf(req->mem_ctx, "%s/%s", private->connectpath, name); + } else { + ret = talloc_asprintf(req->mem_ctx, "%s%s", private->connectpath, name); + } + all_string_sub(ret, "\\", "/", 0); + + strlower(ret); + + return ret; +} + + +/* + read a directory and find all matching file names and stat info + returned names are separate unix and DOS names. The returned names + are relative to the directory +*/ +struct svfs_dir *svfs_list(TALLOC_CTX *mem_ctx, struct request_context *req, const char *pattern) +{ + char *unix_path; + char *p, *mask; + struct svfs_dir *dir; + DIR *odir; + struct dirent *dent; + uint_t allocated = 0; + char *low_mask; + + unix_path = svfs_unix_path(req, pattern); + if (!unix_path) { return NULL; } + + dir = talloc(mem_ctx, sizeof(struct svfs_dir)); + if (!dir) { return NULL; } + + dir->count = 0; + dir->files = 0; + + /* find the base directory */ + p = strrchr(unix_path, '/'); + if (!p) { return NULL; } + + dir->unix_dir = talloc_strndup(mem_ctx, unix_path, PTR_DIFF(p, unix_path)); + if (!dir->unix_dir) { return NULL; } + + /* the wildcard pattern is the last part */ + mask = p+1; + + low_mask = talloc_strdup(mem_ctx, mask); + if (!low_mask) { return NULL; } + strlower(low_mask); + + odir = opendir(dir->unix_dir); + if (!odir) { return NULL; } + + while ((dent = readdir(odir))) { + uint_t i = dir->count; + char *full_name; + char *low_name; + + low_name = talloc_strdup(mem_ctx, dent->d_name); + if (!low_name) { continue; } + strlower(low_name); + + /* check it matches the wildcard pattern */ + if (ms_fnmatch(low_mask, low_name, PROTOCOL_NT1) != 0) { + continue; + } + + if (dir->count >= allocated) { + allocated = (allocated + 100) * 1.2; + dir->files = talloc_realloc(mem_ctx, dir->files, allocated * sizeof(dir->files[0])); + if (!dir->files) { + closedir(odir); + return NULL; + } + } + + dir->files[i].name = low_name; + if (!dir->files[i].name) { continue; } + + asprintf(&full_name, "%s/%s", dir->unix_dir, dir->files[i].name); + if (!full_name) { continue; } + + if (stat(full_name, &dir->files[i].st) == 0) { + dir->count++; + } + + free(full_name); + } + + closedir(odir); + + return dir; +} + + +/* + convert a unix stat struct to a dos attrib +*/ +uint32 svfs_file_attrib(struct stat *st) +{ + if (S_ISDIR(st->st_mode)) { + return FILE_ATTRIBUTE_DIRECTORY; + } + return 0; +} diff --git a/source4/ntvfs/reference/vfs_ref.c b/source4/ntvfs/reference/vfs_ref.c new file mode 100644 index 0000000000..b4fa2e61ff --- /dev/null +++ b/source4/ntvfs/reference/vfs_ref.c @@ -0,0 +1,843 @@ +/* + Unix SMB/CIFS implementation. + + simple NTVFS filesystem backend + + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the 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 implements a very simple NTVFS filesystem backend. + + this backend largely ignores the POSIX -> CIFS mappings, just doing absolutely + minimal work to give a working backend. +*/ + +#include "includes.h" +#include "svfs.h" + +/* + connect to a share - used when a tree_connect operation comes + in. For a disk based backend we needs to ensure that the base + directory exists (tho it doesn't need to be accessible by the user, + that comes later) +*/ +static NTSTATUS svfs_connect(struct request_context *req, const char *sharename) +{ + struct stat st; + struct tcon_context *conn = req->conn; + struct svfs_private *private; + + conn->ntvfs_private = talloc(conn->mem_ctx, sizeof(struct svfs_private)); + + private = conn->ntvfs_private; + + private->connectpath = talloc_strdup(conn->mem_ctx, lp_pathname(conn->service)); + + /* the directory must exist */ + if (stat(private->connectpath, &st) != 0 || !S_ISDIR(st.st_mode)) { + DEBUG(0,("'%s' is not a directory, when connecting to [%s]\n", + private->connectpath, sharename)); + return NT_STATUS_BAD_NETWORK_NAME; + } + + conn->fs_type = talloc_strdup(conn->mem_ctx, "NTFS"); + conn->dev_type = talloc_strdup(conn->mem_ctx, "A:"); + + return NT_STATUS_OK; +} + +/* + disconnect from a share +*/ +static NTSTATUS svfs_disconnect(struct request_context *req) +{ + return NT_STATUS_OK; +} + +/* + delete a file - the dirtype specifies the file types to include in the search. + The name can contain CIFS wildcards, but rarely does (except with OS/2 clients) +*/ +static NTSTATUS svfs_unlink(struct request_context *req, struct smb_unlink *unl) +{ + char *unix_path; + + unix_path = svfs_unix_path(req, unl->in.pattern); + + /* ignoring wildcards ... */ + if (unlink(unix_path) == -1) { + return map_nt_error_from_unix(errno); + } + + return NT_STATUS_OK; +} + + +/* + ioctl interface - we don't do any +*/ +static NTSTATUS svfs_ioctl(struct request_context *req, struct smb_ioctl *io) +{ + return NT_STATUS_INVALID_PARAMETER; +} + +/* + check if a directory exists +*/ +static NTSTATUS svfs_chkpath(struct request_context *req, struct smb_chkpath *cp) +{ + char *unix_path; + struct stat st; + + unix_path = svfs_unix_path(req, cp->in.path); + + if (stat(unix_path, &st) == -1) { + return map_nt_error_from_unix(errno); + } + + if (!S_ISDIR(st.st_mode)) { + return NT_STATUS_NOT_A_DIRECTORY; + } + + return NT_STATUS_OK; +} + +/* + approximately map a struct stat to a fileinfo struct +*/ +static NTSTATUS map_fileinfo(struct request_context *req, union smb_fileinfo *info, struct stat *st) +{ + switch (info->generic.level) { + case SMB_FILEINFO_NETWORK_OPEN_INFORMATION: + unix_to_nt_time(&info->netopen.out.create_time, st->st_ctime); + unix_to_nt_time(&info->netopen.out.access_time, st->st_atime); + unix_to_nt_time(&info->netopen.out.write_time, st->st_mtime); + unix_to_nt_time(&info->netopen.out.change_time, st->st_mtime); + info->netopen.out.alloc_size = st->st_size; + info->netopen.out.size = st->st_size; + info->netopen.out.attrib = svfs_file_attrib(st); + info->netopen.out.unknown = 0; + return NT_STATUS_OK; + + case SMB_FILEINFO_ALL_INFO: + unix_to_nt_time(&info->all_info.out.create_time, st->st_ctime); + unix_to_nt_time(&info->all_info.out.access_time, st->st_atime); + unix_to_nt_time(&info->all_info.out.write_time, st->st_mtime); + unix_to_nt_time(&info->all_info.out.change_time, st->st_mtime); + info->all_info.out.attrib = svfs_file_attrib(st); + info->all_info.out.alloc_size = st->st_size; + info->all_info.out.size = st->st_size; + info->all_info.out.nlink = st->st_nlink; + info->all_info.out.delete_pending = 0; + info->all_info.out.directory = S_ISDIR(st->st_mode) ? 1 : 0; + info->all_info.out.index_number = st->st_ino; + info->all_info.out.ea_size = 0; + info->all_info.out.access_flags = 0xd01BF; /* what is this!? */ + info->all_info.out.current_offset = 0; + info->all_info.out.open_mode = 0; /* what do do put here!? */ + info->all_info.out.alignment_requirement = 0; /* what do do put here!? */ + info->all_info.out.fname = talloc_strdup(req->mem_ctx, "TODO - STORE FILENAME"); + return NT_STATUS_OK; + + case SMB_FILEINFO_BASIC: + unix_to_nt_time(&info->basic.out.create_time, st->st_ctime); + unix_to_nt_time(&info->basic.out.access_time, st->st_atime); + unix_to_nt_time(&info->basic.out.write_time, st->st_mtime); + unix_to_nt_time(&info->basic.out.change_time, st->st_mtime); + info->basic.out.attrib = svfs_file_attrib(st); + return NT_STATUS_OK; + + case SMB_FILEINFO_INFO_STANDARD: + info->info_standard.out.create_time = st->st_ctime; + info->info_standard.out.access_time = st->st_atime; + info->info_standard.out.write_time = st->st_mtime; + info->info_standard.out.size = st->st_size; + info->info_standard.out.alloc_size = st->st_size; + info->info_standard.out.attrib = svfs_file_attrib(st); + return NT_STATUS_OK; + + case SMB_FILEINFO_INFO_STANDARD_EA: + info->info_standard_ea.out.create_time = st->st_ctime; + info->info_standard_ea.out.access_time = st->st_atime; + info->info_standard_ea.out.write_time = st->st_mtime; + info->info_standard_ea.out.size = st->st_size; + info->info_standard_ea.out.alloc_size = st->st_size; + info->info_standard_ea.out.attrib = svfs_file_attrib(st); + info->info_standard_ea.out.ea_size = 0; + return NT_STATUS_OK; + + case SMB_FILEINFO_STANDARD_INFO: + info->standard_info.out.alloc_size = st->st_size; + info->standard_info.out.size = st->st_size; + info->standard_info.out.nlink = st->st_nlink; + info->standard_info.out.delete_pending = 0; + info->standard_info.out.directory = S_ISDIR(st->st_mode) ? 1 : 0; + info->standard_info.out.unknown = 0; + return NT_STATUS_OK; + + case SMB_FILEINFO_INTERNAL: + info->internal.out.device = st->st_dev; + info->internal.out.device = st->st_ino; + return NT_STATUS_OK; + + case SMB_FILEINFO_EA: + info->ea.out.unknown = 0; + return NT_STATUS_OK; + + case SMB_FILEINFO_ATTRIB_TAGINFO: + info->tag.out.attrib = svfs_file_attrib(st); + info->tag.out.reparse_tag = 0; + return NT_STATUS_OK; + + case SMB_FILEINFO_STREAM: + /* setup a single data stream */ + info->stream.out.num_streams = 1; + info->stream.out.streams = talloc(req->mem_ctx, sizeof(info->stream.out.streams[0])); + if (!info->stream.out.streams) { + return NT_STATUS_NO_MEMORY; + } + info->stream.out.streams[0].size = st->st_size; + info->stream.out.streams[0].alloc_size = st->st_size; + info->stream.out.streams[0].stream_name = talloc_strdup(req->mem_ctx,"::$DATA"); + return NT_STATUS_OK; + } + + return NT_STATUS_INVALID_LEVEL; +} + +/* + return info on a pathname +*/ +static NTSTATUS svfs_qpathinfo(struct request_context *req, union smb_fileinfo *info) +{ + char *unix_path; + struct stat st; + + unix_path = svfs_unix_path(req, info->basic.in.fname); + + if (stat(unix_path, &st) == -1) { + return map_nt_error_from_unix(errno); + } + + return map_fileinfo(req, info, &st); +} + +/* + query info on a open file +*/ +static NTSTATUS svfs_qfileinfo(struct request_context *req, union smb_fileinfo *info) +{ + struct stat st; + + if (fstat(info->generic.in.fnum, &st) == -1) { + return map_nt_error_from_unix(errno); + } + + return map_fileinfo(req, info, &st); +} + + +/* + set info on a pathname +*/ +static NTSTATUS svfs_setpathinfo(struct request_context *req, union smb_setfileinfo *st) +{ + return NT_STATUS_NOT_SUPPORTED; +} + +/* + open a file +*/ +static NTSTATUS svfs_open(struct request_context *req, union smb_open *io) +{ + char *unix_path; + struct stat st; + int fd, flags; + + if (io->generic.level != SMB_OPEN_GENERIC) { + return ntvfs_map_open(req, io); + } + + unix_path = svfs_unix_path(req, io->ntcreatex.in.fname); + + switch (io->generic.in.open_disposition) { + case FILE_SUPERSEDE: + flags = O_RDWR | O_CREAT | O_TRUNC; + break; + case FILE_OPEN: + flags = O_RDWR; + break; + case FILE_CREATE: + flags = O_RDWR | O_CREAT | O_EXCL; + break; + case FILE_OPEN_IF: + flags = O_RDWR | O_CREAT; + break; + case FILE_OVERWRITE: + flags = O_RDWR; + break; + case FILE_OVERWRITE_IF: + flags = O_RDWR | O_CREAT | O_TRUNC; + break; + default: + flags = O_RDWR; + break; + } + + if (io->generic.in.create_options & FILE_DIRECTORY_FILE) { + flags = O_RDONLY | O_DIRECTORY; + switch (io->generic.in.open_disposition) { + case FILE_CREATE: + if (mkdir(unix_path, 0755) == -1) { + return map_nt_error_from_unix(errno); + } + break; + case FILE_OPEN_IF: + if (mkdir(unix_path, 0755) == -1 && errno != EEXIST) { + return map_nt_error_from_unix(errno); + } + break; + } + } + + fd = open(unix_path, flags, 0644); + if (fd == -1) { + return map_nt_error_from_unix(errno); + } + + if (fstat(fd, &st) == -1) { + close(fd); + return map_nt_error_from_unix(errno); + } + + ZERO_STRUCT(io->generic.out); + + unix_to_nt_time(&io->generic.out.create_time, st.st_ctime); + unix_to_nt_time(&io->generic.out.access_time, st.st_atime); + unix_to_nt_time(&io->generic.out.write_time, st.st_mtime); + unix_to_nt_time(&io->generic.out.change_time, st.st_mtime); + io->generic.out.fnum = fd; + io->generic.out.alloc_size = st.st_size; + io->generic.out.size = st.st_size; + io->generic.out.file_attr = svfs_file_attrib(&st); + io->generic.out.is_directory = S_ISDIR(st.st_mode) ? 1 : 0; + + return NT_STATUS_OK; +} + +/* + create a directory +*/ +static NTSTATUS svfs_mkdir(struct request_context *req, union smb_mkdir *md) +{ + char *unix_path; + + if (md->generic.level != RAW_MKDIR_MKDIR) { + return NT_STATUS_INVALID_LEVEL; + } + + unix_path = svfs_unix_path(req, md->mkdir.in.path); + + if (mkdir(unix_path, 0777) == -1) { + return map_nt_error_from_unix(errno); + } + + return NT_STATUS_OK; +} + +/* + remove a directory +*/ +static NTSTATUS svfs_rmdir(struct request_context *req, struct smb_rmdir *rd) +{ + char *unix_path; + + unix_path = svfs_unix_path(req, rd->in.path); + + if (rmdir(unix_path) == -1) { + return map_nt_error_from_unix(errno); + } + + return NT_STATUS_OK; +} + +/* + rename a set of files +*/ +static NTSTATUS svfs_rename(struct request_context *req, struct smb_rename *ren) +{ + char *unix_path1, *unix_path2; + + unix_path1 = svfs_unix_path(req, ren->in.pattern1); + unix_path2 = svfs_unix_path(req, ren->in.pattern2); + + if (rename(unix_path1, unix_path2) != 0) { + return map_nt_error_from_unix(errno); + } + + return NT_STATUS_OK; +} + +/* + copy a set of files +*/ +static NTSTATUS svfs_copy(struct request_context *req, struct smb_copy *cp) +{ + return NT_STATUS_NOT_SUPPORTED; +} + +/* + read from a file +*/ +static NTSTATUS svfs_read(struct request_context *req, union smb_read *rd) +{ + ssize_t ret; + + if (rd->generic.level != SMB_READ_READX) { + return NT_STATUS_NOT_SUPPORTED; + } + + ret = pread(rd->readx.in.fnum, + rd->readx.out.data, + rd->readx.in.maxcnt, + rd->readx.in.offset); + if (ret == -1) { + return map_nt_error_from_unix(errno); + } + + rd->readx.out.nread = ret; + rd->readx.out.remaining = 0; /* should fill this in? */ + + return NT_STATUS_OK; +} + +/* + write to a file +*/ +static NTSTATUS svfs_write(struct request_context *req, union smb_write *wr) +{ + ssize_t ret; + + switch (wr->generic.level) { + case SMB_WRITE_WRITEX: + ret = pwrite(wr->writex.in.fnum, + wr->writex.in.data, + wr->writex.in.count, + wr->writex.in.offset); + if (ret == -1) { + return map_nt_error_from_unix(errno); + } + + wr->writex.out.nwritten = ret; + wr->writex.out.remaining = 0; /* should fill this in? */ + + return NT_STATUS_OK; + + case SMB_WRITE_WRITE: + if (wr->write.in.count == 0) { + /* a truncate! */ + ret = ftruncate(wr->write.in.fnum, wr->write.in.offset); + } else { + ret = pwrite(wr->write.in.fnum, + wr->write.in.data, + wr->write.in.count, + wr->write.in.offset); + } + if (ret == -1) { + return map_nt_error_from_unix(errno); + } + + wr->write.out.nwritten = ret; + + return NT_STATUS_OK; + } + + return NT_STATUS_NOT_SUPPORTED; +} + +/* + seek in a file +*/ +static NTSTATUS svfs_seek(struct request_context *req, struct smb_seek *io) +{ + return NT_STATUS_NOT_SUPPORTED; +} + +/* + flush a file +*/ +static NTSTATUS svfs_flush(struct request_context *req, struct smb_flush *io) +{ + fsync(io->in.fnum); + return NT_STATUS_OK; +} + +/* + close a file +*/ +static NTSTATUS svfs_close(struct request_context *req, union smb_close *io) +{ + if (io->generic.level != SMB_CLOSE_CLOSE) { + /* we need a mapping function */ + return NT_STATUS_INVALID_LEVEL; + } + + if (close(io->close.in.fnum) != 0) { + return map_nt_error_from_unix(errno); + } + + return NT_STATUS_OK; +} + +/* + exit - closing files? +*/ +static NTSTATUS svfs_exit(struct request_context *req) +{ + return NT_STATUS_NOT_SUPPORTED; +} + +/* + lock a byte range +*/ +static NTSTATUS svfs_lock(struct request_context *req, union smb_lock *lck) +{ + DEBUG(0,("REWRITE: not doing byte range locking!\n")); + return NT_STATUS_OK; +} + +/* + set info on a open file +*/ +static NTSTATUS svfs_setfileinfo(struct request_context *req, + union smb_setfileinfo *info) +{ + DEBUG(0,("REWRITE: svfs_setfileinfo: not doing setfileinfo level %d\n", + info->generic.level)); + switch (info->generic.level) { + case SMB_SETFILEINFO_BASIC: + info->basic.out.ea_error_offset = 0; + break; + case SMB_SETFILEINFO_END_OF_FILE: + if (ftruncate(info->eof.in.fnum, info->eof.in.size) != 0) { + return map_nt_error_from_unix(errno); + } + info->eof.out.ea_error_offset = 0; + break; + case SMB_SETFILEINFO_ALLOCATION: + info->eof.out.ea_error_offset = 0; + break; + } + return NT_STATUS_OK; +} + + +/* + return filesystem space info +*/ +static NTSTATUS svfs_fsinfo(struct request_context *req, union smb_fsinfo *fs) +{ + struct svfs_private *private = req->conn->ntvfs_private; + + if (fs->generic.level != SMB_FSINFO_GENERIC) { + return ntvfs_map_fsinfo(req, fs); + } + + if (sys_fsusage(private->connectpath, + &fs->generic.out.blocks_free, + &fs->generic.out.blocks_total) == -1) { + return map_nt_error_from_unix(errno); + } + + fs->generic.out.block_size = 512; + + return NT_STATUS_OK; +} + +/* + return filesystem attribute info +*/ +static NTSTATUS svfs_fsattr(struct request_context *req, union smb_fsattr *fs) +{ + struct stat st; + struct svfs_private *private = req->conn->ntvfs_private; + + if (fs->generic.level != SMB_FSATTR_GENERIC) { + return ntvfs_map_fsattr(req, fs); + } + + if (stat(private->connectpath, &st) != 0) { + return map_nt_error_from_unix(errno); + } + + unix_to_nt_time(&fs->generic.out.create_time, st.st_ctime); + fs->generic.out.fs_attr = + FILE_CASE_PRESERVED_NAMES | + FILE_CASE_SENSITIVE_SEARCH | + FILE_PERSISTENT_ACLS; + fs->generic.out.max_file_component_length = 255; + fs->generic.out.serial_number = 1; + fs->generic.out.fs_type = talloc_strdup(req->mem_ctx, "NTFS"); + fs->generic.out.volume_name = talloc_strdup(req->mem_ctx, + lp_servicename(req->conn->service)); + + return NT_STATUS_OK; +} + +/* + return print queue info +*/ +static NTSTATUS svfs_lpq(struct request_context *req, union smb_lpq *lpq) +{ + return NT_STATUS_NOT_SUPPORTED; +} + +/* + list files in a directory matching a wildcard pattern +*/ +NTSTATUS svfs_search_first(struct request_context *req, union smb_search_first *io, + void *search_private, + BOOL (*callback)(void *, union smb_search_data *)) +{ + struct svfs_dir *dir; + int i; + struct svfs_private *private = req->conn->ntvfs_private; + struct search_state *search; + union smb_search_data file; + TALLOC_CTX *mem_ctx; + uint_t max_count; + + if (io->generic.level != SMB_SEARCH_T2FFIRST_BOTH) { + return NT_STATUS_NOT_SUPPORTED; + } + + mem_ctx = talloc_init("svfs_search"); + + search = talloc_zero(mem_ctx, sizeof(struct search_state)); + if (!search) { + return NT_STATUS_NO_MEMORY; + } + + max_count = io->t2ffirst.in.max_count; + + dir = svfs_list(mem_ctx, req, io->t2ffirst.in.pattern); + if (!dir) { + talloc_destroy_pool(mem_ctx); + return NT_STATUS_FOOBAR; + } + + search->mem_ctx = mem_ctx; + search->handle = private->next_search_handle; + search->dir = dir; + + if (dir->count < max_count) { + max_count = dir->count; + } + + for (i=0; i < max_count;i++) { + ZERO_STRUCT(file); + unix_to_nt_time(&file.both.create_time, dir->files[i].st.st_ctime); + unix_to_nt_time(&file.both.access_time, dir->files[i].st.st_atime); + unix_to_nt_time(&file.both.write_time, dir->files[i].st.st_mtime); + unix_to_nt_time(&file.both.change_time, dir->files[i].st.st_mtime); + file.both.name = dir->files[i].name; + file.both.short_name = dir->files[i].name; + file.both.size = dir->files[i].st.st_size; + file.both.ex_attrib = svfs_file_attrib(&dir->files[i].st); + + if (!callback(search_private, &file)) { + break; + } + } + + search->current_index = i; + + io->t2ffirst.out.count = i; + io->t2ffirst.out.handle = search->handle; + io->t2ffirst.out.end_of_search = (i == dir->count) ? 1 : 0; + + /* work out if we are going to keep the search state */ + if ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE) || + ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) && (i == dir->count))) { + talloc_destroy(search->mem_ctx); + } else { + private->next_search_handle++; + DLIST_ADD(private->search, search); + } + + return NT_STATUS_OK; +} + +/* continue a search */ +NTSTATUS svfs_search_next(struct request_context *req, union smb_search_next *io, + void *search_private, + BOOL (*callback)(void *, union smb_search_data *)) +{ + struct svfs_dir *dir; + int i; + struct svfs_private *private = req->conn->ntvfs_private; + struct search_state *search; + union smb_search_data file; + uint_t max_count; + + if (io->generic.level != SMB_SEARCH_T2FFIRST_BOTH) { + return NT_STATUS_NOT_SUPPORTED; + } + + for (search=private->search; search; search = search->next) { + if (search->handle == io->t2fnext.in.handle) break; + } + + if (!search) { + /* we didn't find the search handle */ + return NT_STATUS_FOOBAR; + } + + dir = search->dir; + + /* the client might be asking for something other than just continuing + with the search */ + if (!(io->t2fnext.in.flags & FLAG_TRANS2_FIND_CONTINUE) && + (io->t2fnext.in.flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) && + io->t2fnext.in.last_name && *io->t2fnext.in.last_name) { + /* look backwards first */ + for (i=search->current_index; i > 0; i--) { + if (strcmp(io->t2fnext.in.last_name, dir->files[i-1].name) == 0) { + search->current_index = i; + goto found; + } + } + + /* then look forwards */ + for (i=search->current_index+1; i <= dir->count; i++) { + if (strcmp(io->t2fnext.in.last_name, dir->files[i-1].name) == 0) { + search->current_index = i; + goto found; + } + } + } + +found: + max_count = search->current_index + io->t2fnext.in.max_count; + + if (max_count > dir->count) { + max_count = dir->count; + } + + for (i = search->current_index; i < max_count;i++) { + ZERO_STRUCT(file); + unix_to_nt_time(&file.both.create_time, dir->files[i].st.st_ctime); + unix_to_nt_time(&file.both.access_time, dir->files[i].st.st_atime); + unix_to_nt_time(&file.both.write_time, dir->files[i].st.st_mtime); + unix_to_nt_time(&file.both.change_time, dir->files[i].st.st_mtime); + file.both.name = dir->files[i].name; + file.both.short_name = dir->files[i].name; + file.both.size = dir->files[i].st.st_size; + file.both.ex_attrib = svfs_file_attrib(&dir->files[i].st); + + if (!callback(search_private, &file)) { + break; + } + } + + io->t2fnext.out.count = i - search->current_index; + io->t2fnext.out.end_of_search = (i == dir->count) ? 1 : 0; + + search->current_index = i; + + /* work out if we are going to keep the search state */ + if ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE) || + ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) && (i == dir->count))) { + DLIST_REMOVE(private->search, search); + talloc_destroy(search->mem_ctx); + } + + return NT_STATUS_OK; +} + +/* close a search */ +NTSTATUS svfs_search_close(struct request_context *req, union smb_search_close *io) +{ + struct svfs_private *private = req->conn->ntvfs_private; + struct search_state *search; + + for (search=private->search; search; search = search->next) { + if (search->handle == io->findclose.in.handle) break; + } + + if (!search) { + /* we didn't find the search handle */ + return NT_STATUS_FOOBAR; + } + + DLIST_REMOVE(private->search, search); + talloc_destroy(search->mem_ctx); + + return NT_STATUS_OK; +} + + +/* + initialialise the POSIX disk backend, registering ourselves with the ntvfs subsystem + */ +BOOL posix_vfs_init(void) +{ + BOOL ret; + struct ntvfs_ops ops; + + ZERO_STRUCT(ops); + + /* fill in all the operations */ + ops.connect = svfs_connect; + ops.disconnect = svfs_disconnect; + ops.unlink = svfs_unlink; + ops.chkpath = svfs_chkpath; + ops.qpathinfo = svfs_qpathinfo; + ops.setpathinfo = svfs_setpathinfo; + ops.open = svfs_open; + ops.mkdir = svfs_mkdir; + ops.rmdir = svfs_rmdir; + ops.rename = svfs_rename; + ops.copy = svfs_copy; + ops.ioctl = svfs_ioctl; + ops.read = svfs_read; + ops.write = svfs_write; + ops.seek = svfs_seek; + ops.flush = svfs_flush; + ops.close = svfs_close; + ops.exit = svfs_exit; + ops.lock = svfs_lock; + ops.setfileinfo = svfs_setfileinfo; + ops.qfileinfo = svfs_qfileinfo; + ops.fsinfo = svfs_fsinfo; + ops.fsattr = svfs_fsattr; + ops.lpq = svfs_lpq; + ops.search_first = svfs_search_first; + ops.search_next = svfs_search_next; + ops.search_close = svfs_search_close; + + /* register ourselves with the NTVFS subsystem. We register under the name 'default' + as we wish to be the default backend */ + ret = ntvfs_register("simple", NTVFS_DISK, &ops); + + if (!ret) { + DEBUG(0,("Failed to register POSIX backend!\n")); + return False; + } + + return True; +} diff --git a/source4/ntvfs/simple/svfs.h b/source4/ntvfs/simple/svfs.h new file mode 100644 index 0000000000..e74fd07fce --- /dev/null +++ b/source4/ntvfs/simple/svfs.h @@ -0,0 +1,28 @@ + +struct svfs_private { + /* the base directory */ + char *connectpath; + + /* a linked list of open searches */ + struct search_state *search; + + /* next available search handle */ + uint16 next_search_handle; +}; + +struct svfs_dir { + uint_t count; + char *unix_dir; + struct { + char *name; + struct stat st; + } *files; +}; + +struct search_state { + struct search_state *next, *prev; + TALLOC_CTX *mem_ctx; + uint16 handle; + uint_t current_index; + struct svfs_dir *dir; +}; diff --git a/source4/ntvfs/simple/svfs_util.c b/source4/ntvfs/simple/svfs_util.c new file mode 100644 index 0000000000..a8a88cfad0 --- /dev/null +++ b/source4/ntvfs/simple/svfs_util.c @@ -0,0 +1,162 @@ +/* + Unix SMB/CIFS implementation. + + simple NTVFS filesystem backend + + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +/* + utility functions for simple backend +*/ + +#include "includes.h" +#include "svfs.h" + +/* + convert a windows path to a unix path - don't do any manging or case sensitive handling +*/ +char *svfs_unix_path(struct request_context *req, const char *name) +{ + struct svfs_private *private = req->conn->ntvfs_private; + char *ret; + + if (*name != '\\') { + ret = talloc_asprintf(req->mem_ctx, "%s/%s", private->connectpath, name); + } else { + ret = talloc_asprintf(req->mem_ctx, "%s%s", private->connectpath, name); + } + all_string_sub(ret, "\\", "/", 0); + + strlower(ret); + + return ret; +} + + +/* + read a directory and find all matching file names and stat info + returned names are separate unix and DOS names. The returned names + are relative to the directory +*/ +struct svfs_dir *svfs_list(TALLOC_CTX *mem_ctx, struct request_context *req, const char *pattern) +{ + char *unix_path; + char *p, *mask; + struct svfs_dir *dir; + DIR *odir; + struct dirent *dent; + uint_t allocated = 0; + char *low_mask; + + unix_path = svfs_unix_path(req, pattern); + if (!unix_path) { return NULL; } + + dir = talloc(mem_ctx, sizeof(struct svfs_dir)); + if (!dir) { return NULL; } + + dir->count = 0; + dir->files = 0; + + /* find the base directory */ + p = strrchr(unix_path, '/'); + if (!p) { return NULL; } + + dir->unix_dir = talloc_strndup(mem_ctx, unix_path, PTR_DIFF(p, unix_path)); + if (!dir->unix_dir) { return NULL; } + + /* the wildcard pattern is the last part */ + mask = p+1; + + low_mask = talloc_strdup(mem_ctx, mask); + if (!low_mask) { return NULL; } + strlower(low_mask); + + odir = opendir(dir->unix_dir); + if (!odir) { return NULL; } + + while ((dent = readdir(odir))) { + uint_t i = dir->count; + char *full_name; + char *low_name; + + low_name = talloc_strdup(mem_ctx, dent->d_name); + if (!low_name) { continue; } + strlower(low_name); + + /* check it matches the wildcard pattern */ + if (ms_fnmatch(low_mask, low_name, PROTOCOL_NT1) != 0) { + continue; + } + + if (dir->count >= allocated) { + allocated = (allocated + 100) * 1.2; + dir->files = talloc_realloc(mem_ctx, dir->files, allocated * sizeof(dir->files[0])); + if (!dir->files) { + closedir(odir); + return NULL; + } + } + + dir->files[i].name = low_name; + if (!dir->files[i].name) { continue; } + + asprintf(&full_name, "%s/%s", dir->unix_dir, dir->files[i].name); + if (!full_name) { continue; } + + if (stat(full_name, &dir->files[i].st) == 0) { + dir->count++; + } + + free(full_name); + } + + closedir(odir); + + return dir; +} + + +/******************************************************************* +set the time on a file via file descriptor +*******************************************************************/ +int svfs_file_utime(int fd, struct utimbuf *times) +{ + char *fd_path = NULL; + int ret; + + asprintf(&fd_path, "/proc/self/%d", fd); + if (!fd_path) { + errno = ENOMEM; + return -1; + } + + ret = utime(fd_path, times); + free(fd_path); + return ret; +} + + +/* + map a unix file attrib to a DOS attribute +*/ +uint16 svfs_unix_to_dos_attrib(mode_t mode) +{ + uint16 ret = 0; + if (S_ISDIR(mode)) ret |= FILE_ATTRIBUTE_DIRECTORY; + if (!(mode & S_IWUSR)) ret |= FILE_ATTRIBUTE_READONLY; + return ret; +} diff --git a/source4/ntvfs/simple/vfs_simple.c b/source4/ntvfs/simple/vfs_simple.c new file mode 100644 index 0000000000..caf7732ac7 --- /dev/null +++ b/source4/ntvfs/simple/vfs_simple.c @@ -0,0 +1,848 @@ +/* + Unix SMB/CIFS implementation. + + simple NTVFS filesystem backend + + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the 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 implements a very simple NTVFS filesystem backend. + + this backend largely ignores the POSIX -> CIFS mappings, just doing absolutely + minimal work to give a working backend. +*/ + +#include "includes.h" +#include "svfs.h" + +/* + connect to a share - used when a tree_connect operation comes + in. For a disk based backend we needs to ensure that the base + directory exists (tho it doesn't need to be accessible by the user, + that comes later) +*/ +static NTSTATUS svfs_connect(struct request_context *req, const char *sharename) +{ + struct stat st; + struct tcon_context *conn = req->conn; + struct svfs_private *private; + + conn->ntvfs_private = talloc(conn->mem_ctx, sizeof(struct svfs_private)); + + private = conn->ntvfs_private; + + private->next_search_handle = 0; + private->connectpath = talloc_strdup(conn->mem_ctx, lp_pathname(conn->service)); + + /* the directory must exist */ + if (stat(private->connectpath, &st) != 0 || !S_ISDIR(st.st_mode)) { + DEBUG(0,("'%s' is not a directory, when connecting to [%s]\n", + private->connectpath, sharename)); + return NT_STATUS_BAD_NETWORK_NAME; + } + + conn->fs_type = talloc_strdup(conn->mem_ctx, "NTFS"); + conn->dev_type = talloc_strdup(conn->mem_ctx, "A:"); + + return NT_STATUS_OK; +} + +/* + disconnect from a share +*/ +static NTSTATUS svfs_disconnect(struct tcon_context *req) +{ + return NT_STATUS_OK; +} + +/* + delete a file - the dirtype specifies the file types to include in the search. + The name can contain CIFS wildcards, but rarely does (except with OS/2 clients) +*/ +static NTSTATUS svfs_unlink(struct request_context *req, struct smb_unlink *unl) +{ + char *unix_path; + + unix_path = svfs_unix_path(req, unl->in.pattern); + + /* ignoring wildcards ... */ + if (unlink(unix_path) == -1) { + return map_nt_error_from_unix(errno); + } + + return NT_STATUS_OK; +} + + +/* + ioctl interface - we don't do any +*/ +static NTSTATUS svfs_ioctl(struct request_context *req, struct smb_ioctl *io) +{ + return NT_STATUS_INVALID_PARAMETER; +} + +/* + check if a directory exists +*/ +static NTSTATUS svfs_chkpath(struct request_context *req, struct smb_chkpath *cp) +{ + char *unix_path; + struct stat st; + + unix_path = svfs_unix_path(req, cp->in.path); + + if (stat(unix_path, &st) == -1) { + return map_nt_error_from_unix(errno); + } + + if (!S_ISDIR(st.st_mode)) { + return NT_STATUS_NOT_A_DIRECTORY; + } + + return NT_STATUS_OK; +} + +/* + approximately map a struct stat to a generic fileinfo struct +*/ +static NTSTATUS svfs_map_fileinfo(struct request_context *req, union smb_fileinfo *info, struct stat *st) +{ + unix_to_nt_time(&info->generic.out.create_time, st->st_ctime); + unix_to_nt_time(&info->generic.out.access_time, st->st_atime); + unix_to_nt_time(&info->generic.out.write_time, st->st_mtime); + unix_to_nt_time(&info->generic.out.change_time, st->st_mtime); + info->generic.out.alloc_size = st->st_size; + info->generic.out.size = st->st_size; + info->generic.out.attrib = svfs_unix_to_dos_attrib(st->st_mode); + info->generic.out.alloc_size = st->st_blksize * st->st_blocks; + info->generic.out.nlink = st->st_nlink; + info->generic.out.directory = S_ISDIR(st->st_mode) ? 1 : 0; + info->generic.out.device = st->st_dev; + info->generic.out.inode = st->st_ino; + /* REWRITE: TODO stuff in here */ + info->generic.out.delete_pending = 0; + info->generic.out.ea_size = 0; + info->generic.out.num_eas = 0; + info->generic.out.fname.s = talloc_strdup(req->mem_ctx, "TODO - STORE FILENAME"); + info->generic.out.alt_fname.s = talloc_strdup(req->mem_ctx, "TODO - STORE ALT_FN"); + info->generic.out.ex_attrib = 0; + info->generic.out.compressed_size = 0; + info->generic.out.format = 0; + info->generic.out.unit_shift = 0; + info->generic.out.chunk_shift = 0; + info->generic.out.cluster_shift = 0; + + info->generic.out.access_flags = 0; + info->generic.out.position = 0; + info->generic.out.mode = 0; + info->generic.out.alignment_requirement = 0; + info->generic.out.reparse_tag = 0; + info->generic.out.num_streams = 0; + /* setup a single data stream */ + info->generic.out.num_streams = 1; + info->generic.out.streams = talloc(req->mem_ctx, sizeof(info->stream_info.out.streams[0])); + if (!info->generic.out.streams) { + return NT_STATUS_NO_MEMORY; + } + info->generic.out.streams[0].size = st->st_size; + info->generic.out.streams[0].alloc_size = st->st_size; + info->generic.out.streams[0].stream_name.s = talloc_strdup(req->mem_ctx,"::$DATA"); + /* REWRITE: end */ + + return NT_STATUS_OK; +} + +/* + return info on a pathname +*/ +static NTSTATUS svfs_qpathinfo(struct request_context *req, union smb_fileinfo *info) +{ + char *unix_path; + struct stat st; + + DEBUG(19,("svfs_qpathinfo: file %s level 0x%x\n", info->generic.in.fname, info->generic.level)); + if (info->generic.level != RAW_FILEINFO_GENERIC) { + return ntvfs_map_qpathinfo(req, info); + } + + unix_path = svfs_unix_path(req, info->generic.in.fname); + DEBUG(19,("svfs_qpathinfo: file %s\n", unix_path)); + if (stat(unix_path, &st) == -1) { + DEBUG(19,("svfs_qpathinfo: file %s errno=%d\n", unix_path, errno)); + if (errno == 0) + errno = ENOENT; + return map_nt_error_from_unix(errno); + } + DEBUG(19,("svfs_qpathinfo: file %s, stat done\n", unix_path)); + return svfs_map_fileinfo(req, info, &st); +} + +/* + query info on a open file +*/ +static NTSTATUS svfs_qfileinfo(struct request_context *req, union smb_fileinfo *info) +{ + struct stat st; + + if (info->generic.level != RAW_FILEINFO_GENERIC) { + return ntvfs_map_qfileinfo(req, info); + } + + if (fstat(info->generic.in.fnum, &st) == -1) { + if (errno == 0) + errno = ENOENT; + return map_nt_error_from_unix(errno); + } + + return svfs_map_fileinfo(req, info, &st); +} + + +/* + open a file +*/ +static NTSTATUS svfs_open(struct request_context *req, union smb_open *io) +{ + char *unix_path; + struct stat st; + int fd, flags; + + if (io->generic.level != RAW_OPEN_GENERIC) { + return ntvfs_map_open(req, io); + } + + unix_path = svfs_unix_path(req, io->ntcreatex.in.fname); + + switch (io->generic.in.open_disposition) { + case NTCREATEX_DISP_SUPERSEDE: + flags = O_RDWR | O_CREAT | O_TRUNC; + break; + case NTCREATEX_DISP_OPEN: + flags = O_RDWR; + break; + case NTCREATEX_DISP_CREATE: + flags = O_RDWR | O_CREAT | O_EXCL; + break; + case NTCREATEX_DISP_OPEN_IF: + flags = O_RDWR | O_CREAT; + break; + case NTCREATEX_DISP_OVERWRITE: + flags = O_RDWR; + break; + case NTCREATEX_DISP_OVERWRITE_IF: + flags = O_RDWR | O_CREAT | O_TRUNC; + break; + default: + flags = O_RDWR; + break; + } + + if (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY) { + flags = O_RDONLY | O_DIRECTORY; + switch (io->generic.in.open_disposition) { + case NTCREATEX_DISP_CREATE: + if (mkdir(unix_path, 0755) == -1) { + DEBUG(9,("svfs_open: mkdir %s errno=%d\n", unix_path, errno)); + return map_nt_error_from_unix(errno); + } + break; + case NTCREATEX_DISP_OPEN_IF: + if (mkdir(unix_path, 0755) == -1 && errno != EEXIST) { + DEBUG(9,("svfs_open: mkdir %s errno=%d\n", unix_path, errno)); + return map_nt_error_from_unix(errno); + } + break; + } + } + DEBUG(9,("svfs_open: file %s flags=0x%x\n", unix_path, flags)); + fd = open(unix_path, flags, 0644); + DEBUG(9,("svfs_open: fd=%d errno=%d\n", fd, errno)); + if (fd == -1) { + if (errno == 0) + errno = ENOENT; + return map_nt_error_from_unix(errno); + } + + if (fstat(fd, &st) == -1) { + DEBUG(9,("svfs_open: fstat errno=%d\n", errno)); + if (errno == 0) + errno = ENOENT; + close(fd); + return map_nt_error_from_unix(errno); + } + + ZERO_STRUCT(io->generic.out); + + unix_to_nt_time(&io->generic.out.create_time, st.st_ctime); + unix_to_nt_time(&io->generic.out.access_time, st.st_atime); + unix_to_nt_time(&io->generic.out.write_time, st.st_mtime); + unix_to_nt_time(&io->generic.out.change_time, st.st_mtime); + io->generic.out.fnum = fd; + io->generic.out.alloc_size = st.st_size; + io->generic.out.size = st.st_size; + io->generic.out.attrib = svfs_unix_to_dos_attrib(st.st_mode); + io->generic.out.is_directory = S_ISDIR(st.st_mode) ? 1 : 0; + + return NT_STATUS_OK; +} + +/* + create a directory +*/ +static NTSTATUS svfs_mkdir(struct request_context *req, union smb_mkdir *md) +{ + char *unix_path; + + if (md->generic.level != RAW_MKDIR_MKDIR) { + return NT_STATUS_INVALID_LEVEL; + } + + unix_path = svfs_unix_path(req, md->mkdir.in.path); + + if (mkdir(unix_path, 0777) == -1) { + return map_nt_error_from_unix(errno); + } + + return NT_STATUS_OK; +} + +/* + remove a directory +*/ +static NTSTATUS svfs_rmdir(struct request_context *req, struct smb_rmdir *rd) +{ + char *unix_path; + + unix_path = svfs_unix_path(req, rd->in.path); + + if (rmdir(unix_path) == -1) { + return map_nt_error_from_unix(errno); + } + + return NT_STATUS_OK; +} + +/* + rename a set of files +*/ +static NTSTATUS svfs_rename(struct request_context *req, struct smb_rename *ren) +{ + char *unix_path1, *unix_path2; + + unix_path1 = svfs_unix_path(req, ren->in.pattern1); + unix_path2 = svfs_unix_path(req, ren->in.pattern2); + + if (rename(unix_path1, unix_path2) != 0) { + return map_nt_error_from_unix(errno); + } + + return NT_STATUS_OK; +} + +/* + copy a set of files +*/ +static NTSTATUS svfs_copy(struct request_context *req, struct smb_copy *cp) +{ + return NT_STATUS_NOT_SUPPORTED; +} + +/* + read from a file +*/ +static NTSTATUS svfs_read(struct request_context *req, union smb_read *rd) +{ + ssize_t ret; + + if (rd->generic.level != RAW_READ_READX) { + return NT_STATUS_NOT_SUPPORTED; + } + + ret = pread(rd->readx.in.fnum, + rd->readx.out.data, + rd->readx.in.maxcnt, + rd->readx.in.offset); + if (ret == -1) { + return map_nt_error_from_unix(errno); + } + + rd->readx.out.nread = ret; + rd->readx.out.remaining = 0; /* should fill this in? */ + rd->readx.out.compaction_mode = 0; + + return NT_STATUS_OK; +} + +/* + write to a file +*/ +static NTSTATUS svfs_write(struct request_context *req, union smb_write *wr) +{ + ssize_t ret; + + switch (wr->generic.level) { + case RAW_WRITE_WRITEX: + ret = pwrite(wr->writex.in.fnum, + wr->writex.in.data, + wr->writex.in.count, + wr->writex.in.offset); + if (ret == -1) { + return map_nt_error_from_unix(errno); + } + + wr->writex.out.nwritten = ret; + wr->writex.out.remaining = 0; /* should fill this in? */ + + return NT_STATUS_OK; + + case RAW_WRITE_WRITE: + if (wr->write.in.count == 0) { + /* a truncate! */ + ret = ftruncate(wr->write.in.fnum, wr->write.in.offset); + } else { + ret = pwrite(wr->write.in.fnum, + wr->write.in.data, + wr->write.in.count, + wr->write.in.offset); + } + if (ret == -1) { + return map_nt_error_from_unix(errno); + } + + wr->write.out.nwritten = ret; + + return NT_STATUS_OK; + } + + return NT_STATUS_NOT_SUPPORTED; +} + +/* + seek in a file +*/ +static NTSTATUS svfs_seek(struct request_context *req, struct smb_seek *io) +{ + return NT_STATUS_NOT_SUPPORTED; +} + +/* + flush a file +*/ +static NTSTATUS svfs_flush(struct request_context *req, struct smb_flush *io) +{ + fsync(io->in.fnum); + return NT_STATUS_OK; +} + +/* + close a file +*/ +static NTSTATUS svfs_close(struct request_context *req, union smb_close *io) +{ + if (io->generic.level != RAW_CLOSE_CLOSE) { + /* we need a mapping function */ + return NT_STATUS_INVALID_LEVEL; + } + + if (close(io->close.in.fnum) != 0) { + return map_nt_error_from_unix(errno); + } + + return NT_STATUS_OK; +} + +/* + exit - closing files? +*/ +static NTSTATUS svfs_exit(struct request_context *req) +{ + return NT_STATUS_NOT_SUPPORTED; +} + +/* + lock a byte range +*/ +static NTSTATUS svfs_lock(struct request_context *req, union smb_lock *lck) +{ + DEBUG(0,("REWRITE: not doing byte range locking!\n")); + return NT_STATUS_OK; +} + +/* + set info on a pathname +*/ +static NTSTATUS svfs_setpathinfo(struct request_context *req, union smb_setfileinfo *st) +{ + return NT_STATUS_NOT_SUPPORTED; +} + +/* + set info on a open file +*/ +static NTSTATUS svfs_setfileinfo(struct request_context *req, + union smb_setfileinfo *info) +{ + struct utimbuf unix_times; + int fd; + + switch (info->generic.level) { + case RAW_SFILEINFO_END_OF_FILE_INFO: + case RAW_SFILEINFO_END_OF_FILE_INFORMATION: + if (ftruncate(info->end_of_file_info.file.fnum, + info->end_of_file_info.in.size) != 0) { + return map_nt_error_from_unix(errno); + } + break; + case RAW_SFILEINFO_SETATTRE: + unix_times.actime = info->setattre.in.access_time; + unix_times.modtime = info->setattre.in.write_time; + fd = info->setattre.file.fnum; + + if (unix_times.actime == 0 && unix_times.modtime == 0) { + break; + } + + /* set modify time = to access time if modify time was 0 */ + if (unix_times.actime != 0 && unix_times.modtime == 0) { + unix_times.modtime = unix_times.actime; + } + + /* Set the date on this file */ + if (svfs_file_utime(fd, &unix_times) != 0) { + return NT_STATUS_ACCESS_DENIED; + } + break; + } + return NT_STATUS_OK; +} + + +/* + return filesystem space info +*/ +static NTSTATUS svfs_fsinfo(struct request_context *req, union smb_fsinfo *fs) +{ + struct svfs_private *private = req->conn->ntvfs_private; + struct stat st; + + if (fs->generic.level != RAW_QFS_GENERIC) { + return ntvfs_map_fsinfo(req, fs); + } + + if (sys_fsusage(private->connectpath, + &fs->generic.out.blocks_free, + &fs->generic.out.blocks_total) == -1) { + return map_nt_error_from_unix(errno); + } + + fs->generic.out.block_size = 512; + + if (stat(private->connectpath, &st) != 0) { + return NT_STATUS_DISK_CORRUPT_ERROR; + } + + fs->generic.out.fs_id = st.st_ino; + unix_to_nt_time(&fs->generic.out.create_time, st.st_ctime); + fs->generic.out.serial_number = st.st_ino; + fs->generic.out.fs_attr = 0; + fs->generic.out.max_file_component_length = 255; + fs->generic.out.device_type = 0; + fs->generic.out.device_characteristics = 0; + fs->generic.out.quota_soft = 0; + fs->generic.out.quota_hard = 0; + fs->generic.out.quota_flags = 0; + fs->generic.out.volume_name = talloc_strdup(req->mem_ctx, lp_servicename(req->conn->service)); + fs->generic.out.fs_type = req->conn->fs_type; + + return NT_STATUS_OK; +} + +#if 0 +/* + return filesystem attribute info +*/ +static NTSTATUS svfs_fsattr(struct request_context *req, union smb_fsattr *fs) +{ + struct stat st; + struct svfs_private *private = req->conn->ntvfs_private; + + if (fs->generic.level != RAW_FSATTR_GENERIC) { + return ntvfs_map_fsattr(req, fs); + } + + if (stat(private->connectpath, &st) != 0) { + return map_nt_error_from_unix(errno); + } + + unix_to_nt_time(&fs->generic.out.create_time, st.st_ctime); + fs->generic.out.fs_attr = + FILE_CASE_PRESERVED_NAMES | + FILE_CASE_SENSITIVE_SEARCH | + FILE_PERSISTENT_ACLS; + fs->generic.out.max_file_component_length = 255; + fs->generic.out.serial_number = 1; + fs->generic.out.fs_type = talloc_strdup(req->mem_ctx, "NTFS"); + fs->generic.out.volume_name = talloc_strdup(req->mem_ctx, + lp_servicename(req->conn->service)); + + return NT_STATUS_OK; +} +#endif + +/* + return print queue info +*/ +static NTSTATUS svfs_lpq(struct request_context *req, union smb_lpq *lpq) +{ + return NT_STATUS_NOT_SUPPORTED; +} + +/* + list files in a directory matching a wildcard pattern +*/ +static NTSTATUS svfs_search_first(struct request_context *req, union smb_search_first *io, + void *search_private, + BOOL (*callback)(void *, union smb_search_data *)) +{ + struct svfs_dir *dir; + int i; + struct svfs_private *private = req->conn->ntvfs_private; + struct search_state *search; + union smb_search_data file; + TALLOC_CTX *mem_ctx; + uint_t max_count; + + if (io->generic.level != RAW_SEARCH_BOTH_DIRECTORY_INFO) { + return NT_STATUS_NOT_SUPPORTED; + } + + mem_ctx = talloc_init("svfs_search"); + + search = talloc_zero(mem_ctx, sizeof(struct search_state)); + if (!search) { + return NT_STATUS_NO_MEMORY; + } + + max_count = io->t2ffirst.in.max_count; + + dir = svfs_list(mem_ctx, req, io->t2ffirst.in.pattern); + if (!dir) { + talloc_destroy_pool(mem_ctx); + return NT_STATUS_FOOBAR; + } + + search->mem_ctx = mem_ctx; + search->handle = private->next_search_handle; + search->dir = dir; + + if (dir->count < max_count) { + max_count = dir->count; + } + + for (i=0; i < max_count;i++) { + ZERO_STRUCT(file); + unix_to_nt_time(&file.both_directory_info.create_time, dir->files[i].st.st_ctime); + unix_to_nt_time(&file.both_directory_info.access_time, dir->files[i].st.st_atime); + unix_to_nt_time(&file.both_directory_info.write_time, dir->files[i].st.st_mtime); + unix_to_nt_time(&file.both_directory_info.change_time, dir->files[i].st.st_mtime); + file.both_directory_info.name.s = dir->files[i].name; + file.both_directory_info.short_name.s = dir->files[i].name; + file.both_directory_info.size = dir->files[i].st.st_size; + file.both_directory_info.attrib = svfs_unix_to_dos_attrib(dir->files[i].st.st_mode); + + if (!callback(search_private, &file)) { + break; + } + } + + search->current_index = i; + + io->t2ffirst.out.count = i; + io->t2ffirst.out.handle = search->handle; + io->t2ffirst.out.end_of_search = (i == dir->count) ? 1 : 0; + + /* work out if we are going to keep the search state */ + if ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE) || + ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) && (i == dir->count))) { + talloc_destroy(search->mem_ctx); + } else { + private->next_search_handle++; + DLIST_ADD(private->search, search); + } + + return NT_STATUS_OK; +} + +/* continue a search */ +static NTSTATUS svfs_search_next(struct request_context *req, union smb_search_next *io, + void *search_private, + BOOL (*callback)(void *, union smb_search_data *)) +{ + struct svfs_dir *dir; + int i; + struct svfs_private *private = req->conn->ntvfs_private; + struct search_state *search; + union smb_search_data file; + uint_t max_count; + + if (io->generic.level != RAW_SEARCH_BOTH_DIRECTORY_INFO) { + return NT_STATUS_NOT_SUPPORTED; + } + + for (search=private->search; search; search = search->next) { + if (search->handle == io->t2fnext.in.handle) break; + } + + if (!search) { + /* we didn't find the search handle */ + return NT_STATUS_FOOBAR; + } + + dir = search->dir; + + /* the client might be asking for something other than just continuing + with the search */ + if (!(io->t2fnext.in.flags & FLAG_TRANS2_FIND_CONTINUE) && + (io->t2fnext.in.flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) && + io->t2fnext.in.last_name && *io->t2fnext.in.last_name) { + /* look backwards first */ + for (i=search->current_index; i > 0; i--) { + if (strcmp(io->t2fnext.in.last_name, dir->files[i-1].name) == 0) { + search->current_index = i; + goto found; + } + } + + /* then look forwards */ + for (i=search->current_index+1; i <= dir->count; i++) { + if (strcmp(io->t2fnext.in.last_name, dir->files[i-1].name) == 0) { + search->current_index = i; + goto found; + } + } + } + +found: + max_count = search->current_index + io->t2fnext.in.max_count; + + if (max_count > dir->count) { + max_count = dir->count; + } + + for (i = search->current_index; i < max_count;i++) { + ZERO_STRUCT(file); + unix_to_nt_time(&file.both_directory_info.create_time, dir->files[i].st.st_ctime); + unix_to_nt_time(&file.both_directory_info.access_time, dir->files[i].st.st_atime); + unix_to_nt_time(&file.both_directory_info.write_time, dir->files[i].st.st_mtime); + unix_to_nt_time(&file.both_directory_info.change_time, dir->files[i].st.st_mtime); + file.both_directory_info.name.s = dir->files[i].name; + file.both_directory_info.short_name.s = dir->files[i].name; + file.both_directory_info.size = dir->files[i].st.st_size; + file.both_directory_info.attrib = svfs_unix_to_dos_attrib(dir->files[i].st.st_mode); + + if (!callback(search_private, &file)) { + break; + } + } + + io->t2fnext.out.count = i - search->current_index; + io->t2fnext.out.end_of_search = (i == dir->count) ? 1 : 0; + + search->current_index = i; + + /* work out if we are going to keep the search state */ + if ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE) || + ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) && (i == dir->count))) { + DLIST_REMOVE(private->search, search); + talloc_destroy(search->mem_ctx); + } + + return NT_STATUS_OK; +} + +/* close a search */ +static NTSTATUS svfs_search_close(struct request_context *req, union smb_search_close *io) +{ + struct svfs_private *private = req->conn->ntvfs_private; + struct search_state *search; + + for (search=private->search; search; search = search->next) { + if (search->handle == io->findclose.in.handle) break; + } + + if (!search) { + /* we didn't find the search handle */ + return NT_STATUS_FOOBAR; + } + + DLIST_REMOVE(private->search, search); + talloc_destroy(search->mem_ctx); + + return NT_STATUS_OK; +} + + +/* + initialialise the POSIX disk backend, registering ourselves with the ntvfs subsystem + */ +BOOL posix_vfs_init(void) +{ + BOOL ret; + struct ntvfs_ops ops; + + ZERO_STRUCT(ops); + + /* fill in all the operations */ + ops.connect = svfs_connect; + ops.disconnect = svfs_disconnect; + ops.unlink = svfs_unlink; + ops.chkpath = svfs_chkpath; + ops.qpathinfo = svfs_qpathinfo; + ops.setpathinfo = svfs_setpathinfo; + ops.open = svfs_open; + ops.mkdir = svfs_mkdir; + ops.rmdir = svfs_rmdir; + ops.rename = svfs_rename; + ops.copy = svfs_copy; + ops.ioctl = svfs_ioctl; + ops.read = svfs_read; + ops.write = svfs_write; + ops.seek = svfs_seek; + ops.flush = svfs_flush; + ops.close = svfs_close; + ops.exit = svfs_exit; + ops.lock = svfs_lock; + ops.setfileinfo = svfs_setfileinfo; + ops.qfileinfo = svfs_qfileinfo; + ops.fsinfo = svfs_fsinfo; + ops.lpq = svfs_lpq; + ops.search_first = svfs_search_first; + ops.search_next = svfs_search_next; + ops.search_close = svfs_search_close; + + /* register ourselves with the NTVFS subsystem. We register under the name 'default' + as we wish to be the default backend */ + ret = ntvfs_register("simple", NTVFS_DISK, &ops); + + if (!ret) { + DEBUG(0,("Failed to register POSIX backend!\n")); + return False; + } + + return True; +} diff --git a/source4/pam_smbpass/CHANGELOG b/source4/pam_smbpass/CHANGELOG new file mode 100644 index 0000000000..96ef784008 --- /dev/null +++ b/source4/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/source4/pam_smbpass/INSTALL b/source4/pam_smbpass/INSTALL new file mode 100644 index 0000000000..ae2ba02bbb --- /dev/null +++ b/source4/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 + 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/source4/pam_smbpass/README b/source4/pam_smbpass/README new file mode 100644 index 0000000000..cf208a9914 --- /dev/null +++ b/source4/pam_smbpass/README @@ -0,0 +1,66 @@ +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 one of the other two following modules + + pam_smb - http://www.csn.ul.ie/~airlied/pam_smb/ + authenticates against any remote SMB server + + pam_ntdom - ftp://ftp.samba.org/pub/samba/pam_ntdom/ + authenticates against an NT or Samba domain controller + +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= - 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 , for providing the Linux-PAM +framework, without which none of this would have happened + +* Christian Gafton and Andrew Morgan again, for the +pam_pwdb module upon which pam_smbpass was originally based + +* Luke Leighton 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 +, for creating a great product +and for giving this project a purpose + +--------------------- +Stephen Langasek diff --git a/source4/pam_smbpass/TODO b/source4/pam_smbpass/TODO new file mode 100644 index 0000000000..20cf4fb098 --- /dev/null +++ b/source4/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/source4/pam_smbpass/general.h b/source4/pam_smbpass/general.h new file mode 100644 index 0000000000..4f13d60131 --- /dev/null +++ b/source4/pam_smbpass/general.h @@ -0,0 +1,130 @@ +#ifndef LINUX +/* This is only needed by modules in the Sun implementation. */ +#include +#endif /* LINUX */ + +#include + +#ifndef PAM_AUTHTOK_RECOVER_ERR +#define PAM_AUTHTOK_RECOVER_ERR PAM_AUTHTOK_RECOVERY_ERR +#endif + +#include +#include +#include +#include +#include +#include +#include + +/* + * 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/source4/pam_smbpass/pam_smb_acct.c b/source4/pam_smbpass/pam_smb_acct.c new file mode 100644 index 0000000000..0803ef82a2 --- /dev/null +++ b/source4/pam_smbpass/pam_smb_acct.c @@ -0,0 +1,113 @@ +/* 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 + +#endif /* LINUX */ + +#include + +#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; + + 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 ); + } + + if (!initialize_password_db(True)) { + _log_err( LOG_ALERT, "Cannot access samba password database" ); + return PAM_AUTHINFO_UNAVAIL; + } + + /* Get the user's record. */ + pdb_init_sam(&sampass); + pdb_getsampwnam(sampass, name ); + + if (!sampass) + 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." ); + + return PAM_ACCT_EXPIRED; + } + + /* TODO: support for expired passwords. */ + + 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/source4/pam_smbpass/pam_smb_auth.c b/source4/pam_smbpass/pam_smb_auth.c new file mode 100644 index 0000000000..e5cc12e2f6 --- /dev/null +++ b/source4/pam_smbpass/pam_smb_auth.c @@ -0,0 +1,249 @@ +/* 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 + +#endif /* LINUX */ + +#include + +#include "general.h" + +#include "support.h" + +#define AUTH_RETURN \ +do { \ + 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; + 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 ); + } + + 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, + 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/source4/pam_smbpass/pam_smb_passwd.c b/source4/pam_smbpass/pam_smb_passwd.c new file mode 100644 index 0000000000..91eae3c7a1 --- /dev/null +++ b/source4/pam_smbpass/pam_smb_passwd.c @@ -0,0 +1,330 @@ +/* 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_PASSWORD + +#include "includes.h" + +#ifndef LINUX + +/* This is only used in the Sun implementation. */ +#include + +#endif /* LINUX */ + +#include + +#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, 0, 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; + 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 ); + } + + if (!initialize_password_db(True)) { + _log_err( LOG_ALERT, "Cannot access samba password database" ); + return PAM_AUTHINFO_UNAVAIL; + } + + /* obtain user record */ + if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam(&sampass))) { + return nt_status_to_pam(nt_status); + } + + if (!pdb_getsampwnam(sampass,user)) { + _log_err( LOG_ALERT, "Failed to find entry for user %s.", user ); + 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); + 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); + 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); + 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); + return retval; + + } else if (flags & PAM_UPDATE_AUTHTOK) { + +#if 0 + /* We used to return when this flag was set, but that breaks + password synchronization when /other/ tokens are expired. For + now, we change the password whenever we're asked. SRL */ + if (flags & PAM_CHANGE_EXPIRED_AUTHTOK) { + pdb_free_sam(&sampass); + return PAM_SUCCESS; + } +#endif + /* + * 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); + 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); + 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); + 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) { + /* password updated */ + _log_err( LOG_NOTICE, "password for (%s/%d) changed by (%s/%d)" + , user, pdb_get_uid(sampass), 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); + 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/source4/pam_smbpass/samples/README b/source4/pam_smbpass/samples/README new file mode 100644 index 0000000000..d77603306f --- /dev/null +++ b/source4/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/source4/pam_smbpass/samples/kdc-pdc b/source4/pam_smbpass/samples/kdc-pdc new file mode 100644 index 0000000000..70f1998f32 --- /dev/null +++ b/source4/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/source4/pam_smbpass/samples/password-mature b/source4/pam_smbpass/samples/password-mature new file mode 100644 index 0000000000..6d73e0906f --- /dev/null +++ b/source4/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/source4/pam_smbpass/samples/password-migration b/source4/pam_smbpass/samples/password-migration new file mode 100644 index 0000000000..305cb53858 --- /dev/null +++ b/source4/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/source4/pam_smbpass/samples/password-sync b/source4/pam_smbpass/samples/password-sync new file mode 100644 index 0000000000..0a950dd2e9 --- /dev/null +++ b/source4/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/source4/pam_smbpass/support.c b/source4/pam_smbpass/support.c new file mode 100644 index 0000000000..11de306d13 --- /dev/null +++ b/source4/pam_smbpass/support.c @@ -0,0 +1,637 @@ + /* 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_iconv(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 hash_pass[16]; + 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(%d)" + , uidtoname( getuid() ) + , service ? service : "**unknown**", name + , pdb_get_uid(sampass) ); + 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(%d)" + , uidtoname( getuid() ) + , service ? service : "**unknown**", name + , pdb_get_uid(sampass) ); + new->count = 1; + } + new->user = smbpXstrDup( name ); + new->id = pdb_get_uid(sampass); + 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 + , pdb_get_uid(sampass) ); + } + } else { + _log_err( LOG_NOTICE + , "failed auth request by %s for service %s as %s(%d)" + , uidtoname( getuid() ) + , service ? service : "**unknown**", name + , pdb_get_uid(sampass) ); + 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/source4/pam_smbpass/support.h b/source4/pam_smbpass/support.h new file mode 100644 index 0000000000..a13a2d0aeb --- /dev/null +++ b/source4/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/source4/param/.cvsignore b/source4/param/.cvsignore new file mode 100644 index 0000000000..76e2d7ff3e --- /dev/null +++ b/source4/param/.cvsignore @@ -0,0 +1,3 @@ +*.po32 +*.po + diff --git a/source4/param/loadparm.c b/source4/param/loadparm.c new file mode 100644 index 0000000000..89ba792600 --- /dev/null +++ b/source4/param/loadparm.c @@ -0,0 +1,4136 @@ +/* + 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) Anthony Liguori 2003 + Copyright (C) James Myers 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the 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 */ +static BOOL bLoaded = False; + +#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 + +static BOOL do_parameter(const char *, const char *); + +static BOOL defaults_saved = False; + +struct param_opt { + struct param_opt *prev, *next; + char *key; + char *value; + int flags; +}; + +/* + * This structure describes global (ie., server-wide) parameters. + */ +typedef struct +{ + 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 *szMsgCommand; + char *szHostsEquiv; + char *szServerString; + char *szAutoServices; + char *szPasswdProgram; + char *szPasswdChat; + char *szLogFile; + char *szConfigFile; + char *szSMBPasswdFile; + char *szPrivateDir; + char **szPassdbBackend; + char **szSamBackend; + char **szPreloadModules; + char *szPasswordServer; + char *szSocketOptions; + char *szRealm; + char *szADSserver; + 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; +#ifdef WITH_UTMP + char *szUtmpDir; + char *szWtmpDir; + BOOL bUtmp; +#endif + char *szSourceEnv; + char *szWinbindUID; + char *szWinbindGID; + char *szNonUnixAccountRange; + int AlgorithmicRidBase; + char *szTemplateHomedir; + char *szTemplateShell; + char *szWinbindSeparator; + BOOL bWinbindEnumUsers; + BOOL bWinbindEnumGroups; + BOOL bWinbindUseDefaultDomain; + char *szIDMapBackend; + char *szAddShareCommand; + char *szChangeShareCommand; + char *szDeleteShareCommand; + char *szGuestaccount; + char *szManglingMethod; + int mangle_prefix; + int max_log_size; + int keepalive; + int mangled_stack; + 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 time_offset; + int max_ttl; + int max_wins_ttl; + int min_wins_ttl; + int ReadSize; + int lm_announce; + int lm_interval; + int announce_as; /* This is initialised in init_globals */ + int machine_password_timeout; + int change_notify_timeout; + int stat_cache_size; + int map_to_guest; + int min_passwd_length; + int oplock_break_wait_time; + int winbind_cache_time; + int iLockSpinCount; + int iLockSpinTime; + char *szLdapMachineSuffix; + char *szLdapUserSuffix; +#ifdef WITH_LDAP_SAMCONFIG + int ldap_port; + char *szLdapServer; +#endif + char *socket_options; + int ldap_ssl; + char *szLdapSuffix; + char *szLdapFilter; + char *szLdapAdminDn; + BOOL ldap_trust_ids; + char *szAclCompat; + int ldap_passwd_sync; + BOOL bMsAddPrinterWizard; + BOOL bDNSproxy; + BOOL bWINSsupport; + BOOL bWINSproxy; + BOOL bLocalMaster; + BOOL bPreferredMaster; + BOOL bDomainMaster; + BOOL bDomainLogons; + BOOL bEncryptPasswords; + BOOL bUpdateEncrypt; + BOOL bStripDot; + BOOL bNullPasswords; + BOOL bObeyPamRestrictions; + BOOL bLoadPrinters; + BOOL bLargeReadwrite; + BOOL bReadRaw; + BOOL bWriteRaw; + BOOL bReadPrediction; + BOOL bReadbmpx; + BOOL bSyslogOnly; + BOOL bBrowseList; + BOOL bNISHomeMap; + BOOL bTimeServer; + BOOL bBindInterfacesOnly; + BOOL bPamPasswordChange; + BOOL bUnixPasswdSync; + BOOL bPasswdChatDebug; + 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 bClientUseSpnego; + BOOL bDebugHiresTimestamp; + BOOL bDebugPid; + BOOL bDebugUid; + BOOL bHostMSDfs; + BOOL bHideLocalUsers; + BOOL bUnicode; + BOOL bUseMmap; + BOOL bHostnameLookups; + BOOL bUnixExtensions; + BOOL bDisableNetbios; + BOOL bKernelChangeNotify; + int restrict_anonymous; + int name_cache_timeout; + BOOL client_signing; + struct param_opt *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 *szVfsObjectFile; + char *szVfsOptions; + char *szVfsPath; + char *szMSDfsProxy; + char *ntvfs_handler; + 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 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; + struct param_opt *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 object */ + NULL, /* vfs options */ + NULL, /* vfs path */ + NULL, /* szMSDfsProxy */ + NULL, /* ntvfs_handler */ + 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 */ + 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 */ + + 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_vfs_object(const char *pszParmValue, char **ptr); +static BOOL handle_source_env(const char *pszParmValue, char **ptr); +static BOOL handle_winbind_uid(const char *pszParmValue, char **ptr); +static BOOL handle_winbind_gid(const char *pszParmValue, char **ptr); +static BOOL handle_non_unix_account_range(const char *pszParmValue, char **ptr); + +static BOOL handle_ldap_machine_suffix ( const char *pszParmValue, char **ptr ); +static BOOL handle_ldap_user_suffix ( const char *pszParmValue, char **ptr ); +static BOOL handle_ldap_suffix ( 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 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_SOFTQ, "softq"}, + {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_ON, "Yes"}, + {LDAP_PASSWD_SYNC_ON, "yes"}, + {LDAP_PASSWD_SYNC_ON, "on"}, + {LDAP_PASSWD_SYNC_ON, "On"}, + {LDAP_PASSWD_SYNC_OFF, "no"}, + {LDAP_PASSWD_SYNC_OFF, "No"}, + {LDAP_PASSWD_SYNC_OFF, "off"}, + {LDAP_PASSWD_SYNC_OFF, "Off"}, +#ifdef LDAP_EXOP_X_MODIFY_PASSWD + {LDAP_PASSWD_SYNC_ONLY, "Only"}, + {LDAP_PASSWD_SYNC_ONLY, "only"}, +#endif /* LDAP_EXOP_X_MODIFY_PASSWD */ + {-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} +}; + +/* + 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 + * + * Note: We have a flag called FLAG_DEVELOPER but is not used at this time, it + * is implied in current control logic. This may change at some later time. A + * flag value of 0 means - show as development option only. + * + * 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. + */ +static struct parm_struct parm_table[] = { + {"Base Options", P_SEP, P_SEPARATOR}, + + {"dos charset", P_STRING, P_GLOBAL, &Globals.dos_charset, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"unix charset", P_STRING, P_GLOBAL, &Globals.unix_charset, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"display charset", P_STRING, P_GLOBAL, &Globals.display_charset, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"comment", P_STRING, P_LOCAL, &sDefault.comment, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_SHARE | FLAG_PRINT | FLAG_DEVELOPER}, + {"path", P_STRING, P_LOCAL, &sDefault.szPath, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_SHARE | FLAG_PRINT | FLAG_DEVELOPER}, + {"directory", P_STRING, P_LOCAL, &sDefault.szPath, NULL, NULL, FLAG_HIDE}, + {"workgroup", P_USTRING, P_GLOBAL, &Globals.szWorkgroup, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_WIZARD | FLAG_DEVELOPER}, + {"realm", P_USTRING, P_GLOBAL, &Globals.szRealm, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_WIZARD | FLAG_DEVELOPER}, + {"ADS server", P_STRING, P_GLOBAL, &Globals.szADSserver, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_WIZARD | FLAG_DEVELOPER}, + {"netbios name", P_USTRING, P_GLOBAL, &Globals.szNetbiosName, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_WIZARD | FLAG_DEVELOPER}, + {"netbios aliases", P_LIST, P_GLOBAL, &Globals.szNetbiosAliases, NULL, NULL, FLAG_ADVANCED | FLAG_WIZARD | FLAG_DEVELOPER}, + {"netbios scope", P_USTRING, P_GLOBAL, &Globals.szNetbiosScope, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"server string", P_STRING, P_GLOBAL, &Globals.szServerString, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_DEVELOPER}, + {"interfaces", P_LIST, P_GLOBAL, &Globals.szInterfaces, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_WIZARD | FLAG_DEVELOPER}, + {"bind interfaces only", P_BOOL, P_GLOBAL, &Globals.bBindInterfacesOnly, NULL, NULL, FLAG_ADVANCED | FLAG_WIZARD | FLAG_DEVELOPER}, + {"ntvfs handler", P_STRING, P_LOCAL, &sDefault.ntvfs_handler, NULL, NULL, FLAG_ADVANCED}, + + {"Security Options", P_SEP, P_SEPARATOR}, + + {"security", P_ENUM, P_GLOBAL, &Globals.security, NULL, enum_security, FLAG_BASIC | FLAG_ADVANCED | FLAG_WIZARD | FLAG_DEVELOPER}, + {"auth methods", P_LIST, P_GLOBAL, &Globals.AuthMethods, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_WIZARD | FLAG_DEVELOPER}, + {"encrypt passwords", P_BOOL, P_GLOBAL, &Globals.bEncryptPasswords, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_WIZARD | FLAG_DEVELOPER}, + {"update encrypted", P_BOOL, P_GLOBAL, &Globals.bUpdateEncrypt, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_DEVELOPER}, + {"allow trusted domains", P_BOOL, P_GLOBAL, &Globals.bAllowTrustedDomains, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"hosts equiv", P_STRING, P_GLOBAL, &Globals.szHostsEquiv, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"idmap backend", P_STRING, P_GLOBAL, &Globals.szIDMapBackend, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"min passwd length", P_INTEGER, P_GLOBAL, &Globals.min_passwd_length, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"min password length", P_INTEGER, P_GLOBAL, &Globals.min_passwd_length, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"map to guest", P_ENUM, P_GLOBAL, &Globals.map_to_guest, NULL, enum_map_to_guest, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"null passwords", P_BOOL, P_GLOBAL, &Globals.bNullPasswords, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"obey pam restrictions", P_BOOL, P_GLOBAL, &Globals.bObeyPamRestrictions, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"password server", P_STRING, P_GLOBAL, &Globals.szPasswordServer, NULL, NULL, FLAG_ADVANCED | FLAG_WIZARD | FLAG_DEVELOPER}, + {"smb passwd file", P_STRING, P_GLOBAL, &Globals.szSMBPasswdFile, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"private dir", P_STRING, P_GLOBAL, &Globals.szPrivateDir, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"passdb backend", P_LIST, P_GLOBAL, &Globals.szPassdbBackend, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"sam backend", P_LIST, P_GLOBAL, &Globals.szSamBackend, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"non unix account range", P_STRING, P_GLOBAL, &Globals.szNonUnixAccountRange, handle_non_unix_account_range, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"algorithmic rid base", P_INTEGER, P_GLOBAL, &Globals.AlgorithmicRidBase, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"root directory", P_STRING, P_GLOBAL, &Globals.szRootdir, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"root dir", P_STRING, P_GLOBAL, &Globals.szRootdir, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"root", P_STRING, P_GLOBAL, &Globals.szRootdir, NULL, NULL, FLAG_HIDE | FLAG_DEVELOPER}, + {"guest account", P_STRING, P_GLOBAL, &Globals.szGuestaccount, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_DEVELOPER}, + + {"pam password change", P_BOOL, P_GLOBAL, &Globals.bPamPasswordChange, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"passwd program", P_STRING, P_GLOBAL, &Globals.szPasswdProgram, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"passwd chat", P_STRING, P_GLOBAL, &Globals.szPasswdChat, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"passwd chat debug", P_BOOL, P_GLOBAL, &Globals.bPasswdChatDebug, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"username map", P_STRING, P_GLOBAL, &Globals.szUsernameMap, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER | FLAG_DEVELOPER}, + {"password level", P_INTEGER, P_GLOBAL, &Globals.pwordlevel, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"username level", P_INTEGER, P_GLOBAL, &Globals.unamelevel, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"unix password sync", P_BOOL, P_GLOBAL, &Globals.bUnixPasswdSync, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"restrict anonymous", P_INTEGER, P_GLOBAL, &Globals.restrict_anonymous, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"lanman auth", P_BOOL, P_GLOBAL, &Globals.bLanmanAuth, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"ntlm auth", P_BOOL, P_GLOBAL, &Globals.bNTLMAuth, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"client NTLMv2 auth", P_BOOL, P_GLOBAL, &Globals.bClientNTLMv2Auth, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"client lanman auth", P_BOOL, P_GLOBAL, &Globals.bClientLanManAuth, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + + {"username", P_STRING, P_LOCAL, &sDefault.szUsername, NULL, NULL, 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_GLOBAL | FLAG_SHARE}, + {"valid users", P_LIST, P_LOCAL, &sDefault.szValidUsers, NULL, NULL, FLAG_GLOBAL | FLAG_SHARE}, + {"admin users", P_LIST, P_LOCAL, &sDefault.szAdminUsers, NULL, NULL, FLAG_GLOBAL | FLAG_SHARE}, + {"read list", P_LIST, P_LOCAL, &sDefault.readlist, NULL, NULL, FLAG_GLOBAL | FLAG_SHARE}, + {"write list", P_LIST, P_LOCAL, &sDefault.writelist, NULL, NULL, FLAG_GLOBAL | FLAG_SHARE}, + {"printer admin", P_LIST, P_LOCAL, &sDefault.printer_admin, NULL, NULL, FLAG_GLOBAL | FLAG_PRINT}, + {"force user", P_STRING, P_LOCAL, &sDefault.force_user, NULL, NULL, FLAG_SHARE}, + {"force group", P_STRING, P_LOCAL, &sDefault.force_group, NULL, NULL, FLAG_SHARE}, + {"group", P_STRING, P_LOCAL, &sDefault.force_group, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + + {"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_GLOBAL | FLAG_SHARE}, + {"create mode", P_OCTAL, P_LOCAL, &sDefault.iCreate_mask, NULL, NULL, FLAG_GLOBAL}, + {"force create mode", P_OCTAL, P_LOCAL, &sDefault.iCreate_force_mode, NULL, NULL, FLAG_GLOBAL | FLAG_SHARE}, + {"security mask", P_OCTAL, P_LOCAL, &sDefault.iSecurity_mask, NULL, NULL, FLAG_GLOBAL | FLAG_SHARE}, + {"force security mode", P_OCTAL, P_LOCAL, &sDefault.iSecurity_force_mode, NULL, NULL, FLAG_GLOBAL | FLAG_SHARE}, + {"directory mask", P_OCTAL, P_LOCAL, &sDefault.iDir_mask, NULL, NULL, FLAG_GLOBAL | FLAG_SHARE}, + {"directory mode", P_OCTAL, P_LOCAL, &sDefault.iDir_mask, NULL, NULL, FLAG_GLOBAL}, + {"force directory mode", P_OCTAL, P_LOCAL, &sDefault.iDir_force_mode, NULL, NULL, FLAG_GLOBAL | FLAG_SHARE}, + {"directory security mask", P_OCTAL, P_LOCAL, &sDefault.iDir_Security_mask, NULL, NULL, FLAG_GLOBAL | FLAG_SHARE}, + {"force directory security mode", P_OCTAL, P_LOCAL, &sDefault.iDir_Security_force_mode, NULL, NULL, FLAG_GLOBAL | FLAG_SHARE}, + {"inherit permissions", P_BOOL, P_LOCAL, &sDefault.bInheritPerms, NULL, NULL, FLAG_SHARE}, + {"inherit acls", P_BOOL, P_LOCAL, &sDefault.bInheritACLS, NULL, NULL, FLAG_SHARE}, + {"guest only", P_BOOL, P_LOCAL, &sDefault.bGuest_only, NULL, NULL, 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 | FLAG_DEVELOPER}, + {"public", P_BOOL, P_LOCAL, &sDefault.bGuest_ok, NULL, NULL, FLAG_HIDE}, + + {"only user", P_BOOL, P_LOCAL, &sDefault.bOnlyUser, NULL, NULL, FLAG_SHARE}, + {"hosts allow", P_LIST, P_LOCAL, &sDefault.szHostsallow, NULL, NULL, FLAG_GLOBAL | FLAG_BASIC | FLAG_ADVANCED | FLAG_SHARE | FLAG_PRINT | FLAG_DEVELOPER}, + {"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 | FLAG_DEVELOPER}, + {"deny hosts", P_LIST, P_LOCAL, &sDefault.szHostsdeny, NULL, NULL, FLAG_HIDE}, + {"preload modules", P_LIST, P_GLOBAL, &Globals.szPreloadModules, NULL, NULL, FLAG_BASIC | FLAG_GLOBAL}, + + {"Logging Options", P_SEP, P_SEPARATOR}, + + {"log level", P_INTEGER, P_GLOBAL, &DEBUGLEVEL, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"debuglevel", P_INTEGER, P_GLOBAL, &DEBUGLEVEL, NULL, NULL, FLAG_HIDE}, + {"syslog", P_INTEGER, P_GLOBAL, &Globals.syslog, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"syslog only", P_BOOL, P_GLOBAL, &Globals.bSyslogOnly, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"log file", P_STRING, P_GLOBAL, &Globals.szLogFile, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + + {"max log size", P_INTEGER, P_GLOBAL, &Globals.max_log_size, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"timestamp logs", P_BOOL, P_GLOBAL, &Globals.bTimestampLogs, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"debug timestamp", P_BOOL, P_GLOBAL, &Globals.bTimestampLogs, NULL, NULL, FLAG_DEVELOPER}, + {"debug hires timestamp", P_BOOL, P_GLOBAL, &Globals.bDebugHiresTimestamp, NULL, NULL, FLAG_DEVELOPER}, + {"debug pid", P_BOOL, P_GLOBAL, &Globals.bDebugPid, NULL, NULL, FLAG_DEVELOPER}, + {"debug uid", P_BOOL, P_GLOBAL, &Globals.bDebugUid, NULL, NULL, FLAG_DEVELOPER}, + + {"Protocol Options", P_SEP, P_SEPARATOR}, + + {"smb ports", P_STRING, P_GLOBAL, &Globals.smb_ports, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"large readwrite", P_BOOL, P_GLOBAL, &Globals.bLargeReadwrite, NULL, NULL, FLAG_DEVELOPER}, + {"max protocol", P_ENUM, P_GLOBAL, &Globals.maxprotocol, NULL, enum_protocol, FLAG_DEVELOPER}, + {"min protocol", P_ENUM, P_GLOBAL, &Globals.minprotocol, NULL, enum_protocol, FLAG_DEVELOPER}, + {"unicode", P_BOOL, P_GLOBAL, &Globals.bUnicode, NULL, NULL, FLAG_DEVELOPER}, + {"read bmpx", P_BOOL, P_GLOBAL, &Globals.bReadbmpx, NULL, NULL, FLAG_DEVELOPER}, + {"read raw", P_BOOL, P_GLOBAL, &Globals.bReadRaw, NULL, NULL, FLAG_DEVELOPER}, + {"write raw", P_BOOL, P_GLOBAL, &Globals.bWriteRaw, NULL, NULL, FLAG_DEVELOPER}, + {"disable netbios", P_BOOL, P_GLOBAL, &Globals.bDisableNetbios, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + + {"acl compatibility", P_STRING, P_GLOBAL, &Globals.szAclCompat, handle_acl_compatibility, NULL, FLAG_SHARE | FLAG_GLOBAL | FLAG_ADVANCED}, + {"nt acl support", P_BOOL, P_LOCAL, &sDefault.bNTAclSupport, NULL, NULL, FLAG_GLOBAL | FLAG_SHARE | FLAG_ADVANCED | FLAG_WIZARD}, + {"nt pipe support", P_BOOL, P_GLOBAL, &Globals.bNTPipeSupport, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"nt status support", P_BOOL, P_GLOBAL, &Globals.bNTStatusSupport, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"profile acls", P_BOOL, P_LOCAL, &sDefault.bProfileAcls, NULL, NULL, FLAG_GLOBAL | FLAG_SHARE | FLAG_ADVANCED | FLAG_WIZARD}, + + {"announce version", P_STRING, P_GLOBAL, &Globals.szAnnounceVersion, NULL, NULL, FLAG_DEVELOPER}, + {"announce as", P_ENUM, P_GLOBAL, &Globals.announce_as, NULL, enum_announce_as, FLAG_DEVELOPER}, + {"max mux", P_INTEGER, P_GLOBAL, &Globals.max_mux, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"max xmit", P_INTEGER, P_GLOBAL, &Globals.max_xmit, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + + {"name resolve order", P_STRING, P_GLOBAL, &Globals.szNameResolveOrder, NULL, NULL, FLAG_ADVANCED | FLAG_WIZARD | FLAG_DEVELOPER}, + {"max ttl", P_INTEGER, P_GLOBAL, &Globals.max_ttl, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"max wins ttl", P_INTEGER, P_GLOBAL, &Globals.max_wins_ttl, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"min wins ttl", P_INTEGER, P_GLOBAL, &Globals.min_wins_ttl, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"time server", P_BOOL, P_GLOBAL, &Globals.bTimeServer, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"unix extensions", P_BOOL, P_GLOBAL, &Globals.bUnixExtensions, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"use spnego", P_BOOL, P_GLOBAL, &Globals.bUseSpnego, NULL, NULL, FLAG_DEVELOPER}, + {"client signing", P_BOOL, P_GLOBAL, &Globals.client_signing, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"client use spnego", P_BOOL, P_GLOBAL, &Globals.bClientUseSpnego, NULL, NULL, FLAG_DEVELOPER}, + + {"Tuning Options", P_SEP, P_SEPARATOR}, + + {"block size", P_INTEGER, P_LOCAL, &sDefault.iBlock_size, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL}, + {"change notify timeout", P_INTEGER, P_GLOBAL, &Globals.change_notify_timeout, NULL, NULL, FLAG_DEVELOPER}, + {"deadtime", P_INTEGER, P_GLOBAL, &Globals.deadtime, NULL, NULL, FLAG_DEVELOPER}, + {"keepalive", P_INTEGER, P_GLOBAL, &Globals.keepalive, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"kernel change notify", P_BOOL, P_GLOBAL, &Globals.bKernelChangeNotify, NULL, NULL, FLAG_DEVELOPER}, + + {"lpq cache time", P_INTEGER, P_GLOBAL, &Globals.lpqcachetime, NULL, NULL, FLAG_DEVELOPER}, + {"max smbd processes", P_INTEGER, P_GLOBAL, &Globals.iMaxSmbdProcesses, NULL, NULL, FLAG_DEVELOPER}, + {"max connections", P_INTEGER, P_LOCAL, &sDefault.iMaxConnections, NULL, NULL, FLAG_SHARE}, + {"paranoid server security", P_BOOL, P_GLOBAL, &Globals.paranoid_server_security, NULL, NULL, FLAG_DEVELOPER}, + {"max disk size", P_INTEGER, P_GLOBAL, &Globals.maxdisksize, NULL, NULL, FLAG_DEVELOPER}, + {"max open files", P_INTEGER, P_GLOBAL, &Globals.max_open_files, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"min print space", P_INTEGER, P_LOCAL, &sDefault.iMinPrintSpace, NULL, NULL, FLAG_PRINT}, + {"read size", P_INTEGER, P_GLOBAL, &Globals.ReadSize, NULL, NULL, FLAG_DEVELOPER}, + + {"socket options", P_STRING, P_GLOBAL, &Globals.socket_options, NULL, NULL, FLAG_DEVELOPER}, + {"stat cache size", P_INTEGER, P_GLOBAL, &Globals.stat_cache_size, NULL, NULL, FLAG_DEVELOPER}, + {"strict allocate", P_BOOL, P_LOCAL, &sDefault.bStrictAllocate, NULL, NULL, FLAG_SHARE}, + {"strict sync", P_BOOL, P_LOCAL, &sDefault.bStrictSync, NULL, NULL, FLAG_SHARE}, + {"sync always", P_BOOL, P_LOCAL, &sDefault.bSyncAlways, NULL, NULL, FLAG_SHARE}, + {"use mmap", P_BOOL, P_GLOBAL, &Globals.bUseMmap, NULL, NULL, FLAG_DEVELOPER}, + {"use sendfile", P_BOOL, P_LOCAL, &sDefault.bUseSendfile, NULL, NULL, FLAG_SHARE}, + {"hostname lookups", P_BOOL, P_GLOBAL, &Globals.bHostnameLookups, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"write cache size", P_INTEGER, P_LOCAL, &sDefault.iWriteCacheSize, NULL, NULL, FLAG_SHARE}, + + {"name cache timeout", P_INTEGER, P_GLOBAL, &Globals.name_cache_timeout, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + + {"Printing Options", P_SEP, P_SEPARATOR}, + + {"total print jobs", P_INTEGER, P_GLOBAL, &Globals.iTotalPrintJobs, NULL, NULL, FLAG_PRINT}, + {"max reported print jobs", P_INTEGER, P_LOCAL, &sDefault.iMaxReportedPrintJobs, NULL, NULL, FLAG_PRINT}, + {"max print jobs", P_INTEGER, P_LOCAL, &sDefault.iMaxPrintJobs, NULL, NULL, FLAG_PRINT}, + {"load printers", P_BOOL, P_GLOBAL, &Globals.bLoadPrinters, NULL, NULL, FLAG_PRINT}, + {"printcap name", P_STRING, P_GLOBAL, &Globals.szPrintcapname, NULL, NULL, FLAG_PRINT | FLAG_DEVELOPER}, + {"printcap", P_STRING, P_GLOBAL, &Globals.szPrintcapname, NULL, NULL, FLAG_HIDE}, + {"printable", P_BOOL, P_LOCAL, &sDefault.bPrint_ok, NULL, NULL, 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_PRINT | FLAG_GLOBAL}, + {"print command", P_STRING, P_LOCAL, &sDefault.szPrintcommand, NULL, NULL, FLAG_PRINT | FLAG_GLOBAL}, + {"disable spoolss", P_BOOL, P_GLOBAL, &Globals.bDisableSpoolss, NULL, NULL, FLAG_PRINT | FLAG_GLOBAL}, + {"lpq command", P_STRING, P_LOCAL, &sDefault.szLpqcommand, NULL, NULL, FLAG_PRINT | FLAG_GLOBAL}, + {"lprm command", P_STRING, P_LOCAL, &sDefault.szLprmcommand, NULL, NULL, FLAG_PRINT | FLAG_GLOBAL}, + {"lppause command", P_STRING, P_LOCAL, &sDefault.szLppausecommand, NULL, NULL, FLAG_PRINT | FLAG_GLOBAL}, + {"lpresume command", P_STRING, P_LOCAL, &sDefault.szLpresumecommand, NULL, NULL, FLAG_PRINT | FLAG_GLOBAL}, + {"queuepause command", P_STRING, P_LOCAL, &sDefault.szQueuepausecommand, NULL, NULL, FLAG_PRINT | FLAG_GLOBAL}, + {"queueresume command", P_STRING, P_LOCAL, &sDefault.szQueueresumecommand, NULL, NULL, FLAG_PRINT | FLAG_GLOBAL}, + + {"enumports command", P_STRING, P_GLOBAL, &Globals.szEnumPortsCommand, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"addprinter command", P_STRING, P_GLOBAL, &Globals.szAddPrinterCommand, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"deleteprinter command", P_STRING, P_GLOBAL, &Globals.szDeletePrinterCommand, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"show add printer wizard", P_BOOL, P_GLOBAL, &Globals.bMsAddPrinterWizard, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"os2 driver map", P_STRING, P_GLOBAL, &Globals.szOs2DriverMap, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + + {"printer name", P_STRING, P_LOCAL, &sDefault.szPrintername, NULL, NULL, 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_PRINT}, + {"default devmode", P_BOOL, P_LOCAL, &sDefault.bDefaultDevmode, NULL, NULL, FLAG_PRINT}, + + {"Filename Handling", P_SEP, P_SEPARATOR}, + {"strip dot", P_BOOL, P_GLOBAL, &Globals.bStripDot, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"mangling method", P_STRING, P_GLOBAL, &Globals.szManglingMethod, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"mangle prefix", P_INTEGER, P_GLOBAL, &Globals.mangle_prefix, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + + {"mangled stack", P_INTEGER, P_GLOBAL, &Globals.mangled_stack, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"default case", P_ENUM, P_LOCAL, &sDefault.iDefaultCase, NULL, enum_case, FLAG_SHARE}, + {"case sensitive", P_BOOL, P_LOCAL, &sDefault.bCaseSensitive, NULL, NULL, 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_SHARE | FLAG_GLOBAL}, + {"short preserve case", P_BOOL, P_LOCAL, &sDefault.bShortCasePreserve, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL}, + {"mangle case", P_BOOL, P_LOCAL, &sDefault.bCaseMangle, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL}, + {"mangling char", P_CHAR, P_LOCAL, &sDefault.magic_char, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL}, + {"hide dot files", P_BOOL, P_LOCAL, &sDefault.bHideDotFiles, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL}, + {"hide special files", P_BOOL, P_LOCAL, &sDefault.bHideSpecialFiles, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL}, + {"hide unreadable", P_BOOL, P_LOCAL, &sDefault.bHideUnReadable, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL}, + {"hide unwriteable files", P_BOOL, P_LOCAL, &sDefault.bHideUnWriteableFiles, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL}, + {"delete veto files", P_BOOL, P_LOCAL, &sDefault.bDeleteVetoFiles, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL}, + {"veto files", P_STRING, P_LOCAL, &sDefault.szVetoFiles, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL }, + {"hide files", P_STRING, P_LOCAL, &sDefault.szHideFiles, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL }, + {"veto oplock files", P_STRING, P_LOCAL, &sDefault.szVetoOplockFiles, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL }, + {"map system", P_BOOL, P_LOCAL, &sDefault.bMap_system, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL}, + {"map hidden", P_BOOL, P_LOCAL, &sDefault.bMap_hidden, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL}, + {"map archive", P_BOOL, P_LOCAL, &sDefault.bMap_archive, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL}, + {"mangled names", P_BOOL, P_LOCAL, &sDefault.bMangledNames, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL}, + {"mangled map", P_STRING, P_LOCAL, &sDefault.szMangledMap, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL}, + {"stat cache", P_BOOL, P_GLOBAL, &Globals.bStatCache, NULL, NULL, FLAG_DEVELOPER}, + + {"Domain Options", P_SEP, P_SEPARATOR}, + + {"machine password timeout", P_INTEGER, P_GLOBAL, &Globals.machine_password_timeout, NULL, NULL, FLAG_ADVANCED | FLAG_WIZARD | FLAG_DEVELOPER}, + + {"Logon Options", P_SEP, P_SEPARATOR}, + + {"add user script", P_STRING, P_GLOBAL, &Globals.szAddUserScript, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"delete user script", P_STRING, P_GLOBAL, &Globals.szDelUserScript, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"add group script", P_STRING, P_GLOBAL, &Globals.szAddGroupScript, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"delete group script", P_STRING, P_GLOBAL, &Globals.szDelGroupScript, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"add user to group script", P_STRING, P_GLOBAL, &Globals.szAddUserToGroupScript, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"delete user from group script", P_STRING, P_GLOBAL, &Globals.szDelUserFromGroupScript, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"set primary group script", P_STRING, P_GLOBAL, &Globals.szSetPrimaryGroupScript, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"add machine script", P_STRING, P_GLOBAL, &Globals.szAddMachineScript, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"shutdown script", P_STRING, P_GLOBAL, &Globals.szShutdownScript, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"abort shutdown script", P_STRING, P_GLOBAL, &Globals.szAbortShutdownScript, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + + {"logon script", P_STRING, P_GLOBAL, &Globals.szLogonScript, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"logon path", P_STRING, P_GLOBAL, &Globals.szLogonPath, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"logon drive", P_STRING, P_GLOBAL, &Globals.szLogonDrive, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"logon home", P_STRING, P_GLOBAL, &Globals.szLogonHome, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"domain logons", P_BOOL, P_GLOBAL, &Globals.bDomainLogons, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + + {"Browse Options", P_SEP, P_SEPARATOR}, + + {"os level", P_INTEGER, P_GLOBAL, &Globals.os_level, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_DEVELOPER}, + {"lm announce", P_ENUM, P_GLOBAL, &Globals.lm_announce, NULL, enum_bool_auto, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"lm interval", P_INTEGER, P_GLOBAL, &Globals.lm_interval, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"preferred master", P_ENUM, P_GLOBAL, &Globals.bPreferredMaster, NULL, enum_bool_auto, FLAG_BASIC | FLAG_ADVANCED | FLAG_DEVELOPER}, + {"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 | FLAG_DEVELOPER}, + {"domain master", P_ENUM, P_GLOBAL, &Globals.bDomainMaster, NULL, enum_bool_auto, FLAG_BASIC | FLAG_ADVANCED | FLAG_DEVELOPER}, + {"browse list", P_BOOL, P_GLOBAL, &Globals.bBrowseList, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"browseable", P_BOOL, P_LOCAL, &sDefault.bBrowseable, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_SHARE | FLAG_PRINT | FLAG_DEVELOPER}, + {"browsable", P_BOOL, P_LOCAL, &sDefault.bBrowseable, NULL, NULL, FLAG_HIDE}, + {"enhanced browsing", P_BOOL, P_GLOBAL, &Globals.enhanced_browsing, NULL, NULL, FLAG_DEVELOPER | FLAG_ADVANCED}, + + {"WINS Options", P_SEP, P_SEPARATOR}, + {"dns proxy", P_BOOL, P_GLOBAL, &Globals.bDNSproxy, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"wins proxy", P_BOOL, P_GLOBAL, &Globals.bWINSproxy, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + + {"wins server", P_LIST, P_GLOBAL, &Globals.szWINSservers, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_WIZARD | FLAG_DEVELOPER}, + {"wins support", P_BOOL, P_GLOBAL, &Globals.bWINSsupport, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_WIZARD | FLAG_DEVELOPER}, + {"wins hook", P_STRING, P_GLOBAL, &Globals.szWINSHook, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"wins partners", P_STRING, P_GLOBAL, &Globals.szWINSPartners, NULL, NULL, FLAG_ADVANCED | FLAG_WIZARD | FLAG_DEVELOPER}, + + {"Locking Options", P_SEP, P_SEPARATOR}, + + {"blocking locks", P_BOOL, P_LOCAL, &sDefault.bBlockingLocks, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL}, + {"csc policy", P_ENUM, P_LOCAL, &sDefault.iCSCPolicy, NULL, enum_csc_policy, FLAG_SHARE | FLAG_GLOBAL}, + {"fake oplocks", P_BOOL, P_LOCAL, &sDefault.bFakeOplocks, NULL, NULL, FLAG_SHARE}, + {"kernel oplocks", P_BOOL, P_GLOBAL, &Globals.bKernelOplocks, NULL, NULL, FLAG_GLOBAL}, + {"locking", P_BOOL, P_LOCAL, &sDefault.bLocking, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL}, + {"lock spin count", P_INTEGER, P_GLOBAL, &Globals.iLockSpinCount, NULL, NULL, FLAG_GLOBAL}, + {"lock spin time", P_INTEGER, P_GLOBAL, &Globals.iLockSpinTime, NULL, NULL, FLAG_GLOBAL}, + + {"oplocks", P_BOOL, P_LOCAL, &sDefault.bOpLocks, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL}, + {"level2 oplocks", P_BOOL, P_LOCAL, &sDefault.bLevel2OpLocks, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL}, + {"oplock break wait time", P_INTEGER, P_GLOBAL, &Globals.oplock_break_wait_time, NULL, NULL, FLAG_GLOBAL}, + {"oplock contention limit", P_INTEGER, P_LOCAL, &sDefault.iOplockContentionLimit, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL}, + {"posix locking", P_BOOL, P_LOCAL, &sDefault.bPosixLocking, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL}, + {"strict locking", P_BOOL, P_LOCAL, &sDefault.bStrictLocking, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL}, + {"share modes", P_BOOL, P_LOCAL, &sDefault.bShareModes, NULL, NULL, FLAG_SHARE|FLAG_GLOBAL}, + + {"Ldap Options", P_SEP, P_SEPARATOR}, + +#ifdef WITH_LDAP_SAMCONFIG + {"ldap server", P_STRING, P_GLOBAL, &Globals.szLdapServer, NULL, NULL, 0}, + {"ldap port", P_INTEGER, P_GLOBAL, &Globals.ldap_port, NULL, NULL, 0}, +#endif + {"ldap suffix", P_STRING, P_GLOBAL, &Globals.szLdapSuffix, handle_ldap_suffix, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"ldap machine suffix", P_STRING, P_GLOBAL, &Globals.szLdapMachineSuffix, handle_ldap_machine_suffix, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"ldap user suffix", P_STRING, P_GLOBAL, &Globals.szLdapUserSuffix, handle_ldap_user_suffix, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"ldap filter", P_STRING, P_GLOBAL, &Globals.szLdapFilter, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"ldap admin dn", P_STRING, P_GLOBAL, &Globals.szLdapAdminDn, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"ldap ssl", P_ENUM, P_GLOBAL, &Globals.ldap_ssl, NULL, enum_ldap_ssl, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"ldap passwd sync", P_ENUM, P_GLOBAL, &Globals.ldap_passwd_sync, NULL, enum_ldap_passwd_sync, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"ldap trust ids", P_BOOL, P_GLOBAL, &Globals.ldap_trust_ids, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + + {"Miscellaneous Options", P_SEP, P_SEPARATOR}, + {"add share command", P_STRING, P_GLOBAL, &Globals.szAddShareCommand, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"change share command", P_STRING, P_GLOBAL, &Globals.szChangeShareCommand, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"delete share command", P_STRING, P_GLOBAL, &Globals.szDeleteShareCommand, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + + {"config file", P_STRING, P_GLOBAL, &Globals.szConfigFile, NULL, NULL, FLAG_HIDE}, + {"preload", P_STRING, P_GLOBAL, &Globals.szAutoServices, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"auto services", P_STRING, P_GLOBAL, &Globals.szAutoServices, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"lock dir", P_STRING, P_GLOBAL, &Globals.szLockDir, NULL, NULL, FLAG_HIDE}, + {"lock directory", P_STRING, P_GLOBAL, &Globals.szLockDir, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"pid directory", P_STRING, P_GLOBAL, &Globals.szPidDir, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, +#ifdef WITH_UTMP + {"utmp directory", P_STRING, P_GLOBAL, &Globals.szUtmpDir, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"wtmp directory", P_STRING, P_GLOBAL, &Globals.szWtmpDir, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"utmp", P_BOOL, P_GLOBAL, &Globals.bUtmp, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, +#endif + + {"default service", P_STRING, P_GLOBAL, &Globals.szDefaultService, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"default", P_STRING, P_GLOBAL, &Globals.szDefaultService, NULL, NULL, FLAG_DEVELOPER}, + {"message command", P_STRING, P_GLOBAL, &Globals.szMsgCommand, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"dfree command", P_STRING, P_GLOBAL, &Globals.szDfree, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"remote announce", P_STRING, P_GLOBAL, &Globals.szRemoteAnnounce, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"remote browse sync", P_STRING, P_GLOBAL, &Globals.szRemoteBrowseSync, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"socket address", P_STRING, P_GLOBAL, &Globals.szSocketAddress, NULL, NULL, FLAG_DEVELOPER}, + {"homedir map", P_STRING, P_GLOBAL, &Globals.szNISHomeMapName, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"time offset", P_INTEGER, P_GLOBAL, &Globals.time_offset, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"NIS homedir", P_BOOL, P_GLOBAL, &Globals.bNISHomeMap, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"-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_SHARE | FLAG_PRINT}, + {"preexec", P_STRING, P_LOCAL, &sDefault.szPreExec, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + + {"preexec close", P_BOOL, P_LOCAL, &sDefault.bPreexecClose, NULL, NULL, FLAG_SHARE}, + {"postexec", P_STRING, P_LOCAL, &sDefault.szPostExec, NULL, NULL, FLAG_SHARE | FLAG_PRINT}, + {"root preexec", P_STRING, P_LOCAL, &sDefault.szRootPreExec, NULL, NULL, FLAG_SHARE | FLAG_PRINT}, + {"root preexec close", P_BOOL, P_LOCAL, &sDefault.bRootpreexecClose, NULL, NULL, FLAG_SHARE}, + {"root postexec", P_STRING, P_LOCAL, &sDefault.szRootPostExec, NULL, NULL, 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_SHARE }, + {"fstype", P_STRING, P_LOCAL, &sDefault.fstype, NULL, NULL, FLAG_SHARE}, + {"set directory", P_BOOLREV, P_LOCAL, &sDefault.bNo_set_dir, NULL, NULL, FLAG_SHARE}, + {"source environment", P_STRING, P_GLOBAL, &Globals.szSourceEnv, handle_source_env, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"wide links", P_BOOL, P_LOCAL, &sDefault.bWidelinks, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL}, + {"follow symlinks", P_BOOL, P_LOCAL, &sDefault.bSymlinks, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL}, + {"dont descend", P_STRING, P_LOCAL, &sDefault.szDontdescend, NULL, NULL, FLAG_SHARE}, + {"magic script", P_STRING, P_LOCAL, &sDefault.szMagicScript, NULL, NULL, FLAG_SHARE}, + {"magic output", P_STRING, P_LOCAL, &sDefault.szMagicOutput, NULL, NULL, FLAG_SHARE}, + {"delete readonly", P_BOOL, P_LOCAL, &sDefault.bDeleteReadonly, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL}, + {"dos filemode", P_BOOL, P_LOCAL, &sDefault.bDosFilemode, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL}, + {"dos filetimes", P_BOOL, P_LOCAL, &sDefault.bDosFiletimes, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL}, + {"dos filetime resolution", P_BOOL, P_LOCAL, &sDefault.bDosFiletimeResolution, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL}, + + {"fake directory create times", P_BOOL, P_LOCAL, &sDefault.bFakeDirCreateTimes, NULL, NULL, FLAG_SHARE | FLAG_GLOBAL}, + {"panic action", P_STRING, P_GLOBAL, &Globals.szPanicAction, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"hide local users", P_BOOL, P_GLOBAL, &Globals.bHideLocalUsers, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + + {"VFS module options", P_SEP, P_SEPARATOR}, + + {"vfs object", P_STRING, P_LOCAL, &sDefault.szVfsObjectFile, handle_vfs_object, NULL, FLAG_SHARE}, + {"vfs options", P_STRING, P_LOCAL, &sDefault.szVfsOptions, NULL, NULL, FLAG_SHARE}, + {"vfs path", P_STRING, P_LOCAL, &sDefault.szVfsPath, NULL, NULL, FLAG_SHARE}, + + + {"msdfs root", P_BOOL, P_LOCAL, &sDefault.bMSDfsRoot, NULL, NULL, FLAG_SHARE}, + {"msdfs proxy", P_STRING, P_LOCAL, &sDefault.szMSDfsProxy, NULL, NULL, FLAG_SHARE}, + {"host msdfs", P_BOOL, P_GLOBAL, &Globals.bHostMSDfs, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"Winbind options", P_SEP, P_SEPARATOR}, + + {"winbind uid", P_STRING, P_GLOBAL, &Globals.szWinbindUID, handle_winbind_uid, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"winbind gid", P_STRING, P_GLOBAL, &Globals.szWinbindGID, handle_winbind_gid, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"template homedir", P_STRING, P_GLOBAL, &Globals.szTemplateHomedir, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"template shell", P_STRING, P_GLOBAL, &Globals.szTemplateShell, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"winbind separator", P_STRING, P_GLOBAL, &Globals.szWinbindSeparator, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"winbind cache time", P_INTEGER, P_GLOBAL, &Globals.winbind_cache_time, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"winbind enum users", P_BOOL, P_GLOBAL, &Globals.bWinbindEnumUsers, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"winbind enum groups", P_BOOL, P_GLOBAL, &Globals.bWinbindEnumGroups, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + {"winbind use default domain", P_BOOL, P_GLOBAL, &Globals.bWinbindUseDefaultDomain, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER}, + + {NULL, P_BOOL, P_NONE, NULL, NULL, NULL, 0} +}; + +/*************************************************************************** + Initialise the sDefault parameter structure for the printer values. +***************************************************************************/ + +static void init_printer_values(void) +{ + /* choose defaults depending on the type of printing */ + switch (sDefault.iPrinting) { + case PRINT_BSD: + case PRINT_AIX: + case PRINT_LPRNT: + case PRINT_LPROS2: + string_set(&sDefault.szLpqcommand, "lpq -P'%p'"); + string_set(&sDefault.szLprmcommand, "lprm -P'%p' %j"); + string_set(&sDefault.szPrintcommand, + "lpr -r -P'%p' %s"); + break; + + case PRINT_LPRNG: + case PRINT_PLP: + string_set(&sDefault.szLpqcommand, "lpq -P'%p'"); + string_set(&sDefault.szLprmcommand, "lprm -P'%p' %j"); + string_set(&sDefault.szPrintcommand, + "lpr -r -P'%p' %s"); + string_set(&sDefault.szQueuepausecommand, + "lpc stop '%p'"); + string_set(&sDefault.szQueueresumecommand, + "lpc start '%p'"); + string_set(&sDefault.szLppausecommand, + "lpc hold '%p' %j"); + string_set(&sDefault.szLpresumecommand, + "lpc release '%p' %j"); + break; + + case PRINT_CUPS: +#ifdef HAVE_CUPS + string_set(&sDefault.szLpqcommand, ""); + string_set(&sDefault.szLprmcommand, ""); + string_set(&sDefault.szPrintcommand, ""); + string_set(&sDefault.szLppausecommand, ""); + string_set(&sDefault.szLpresumecommand, ""); + string_set(&sDefault.szQueuepausecommand, ""); + string_set(&sDefault.szQueueresumecommand, ""); + + string_set(&Globals.szPrintcapname, "cups"); +#else + string_set(&sDefault.szLpqcommand, + "/usr/bin/lpstat -o '%p'"); + string_set(&sDefault.szLprmcommand, + "/usr/bin/cancel '%p-%j'"); + string_set(&sDefault.szPrintcommand, + "/usr/bin/lp -d '%p' %s; rm %s"); + string_set(&sDefault.szLppausecommand, + "lp -i '%p-%j' -H hold"); + string_set(&sDefault.szLpresumecommand, + "lp -i '%p-%j' -H resume"); + string_set(&sDefault.szQueuepausecommand, + "/usr/bin/disable '%p'"); + string_set(&sDefault.szQueueresumecommand, + "/usr/bin/enable '%p'"); + string_set(&Globals.szPrintcapname, "lpstat"); +#endif /* HAVE_CUPS */ + break; + + case PRINT_SYSV: + case PRINT_HPUX: + string_set(&sDefault.szLpqcommand, "lpstat -o%p"); + string_set(&sDefault.szLprmcommand, "cancel %p-%j"); + string_set(&sDefault.szPrintcommand, + "lp -c -d%p %s; rm %s"); + string_set(&sDefault.szQueuepausecommand, + "disable %p"); + string_set(&sDefault.szQueueresumecommand, + "enable %p"); +#ifndef HPUX + string_set(&sDefault.szLppausecommand, + "lp -i %p-%j -H hold"); + string_set(&sDefault.szLpresumecommand, + "lp -i %p-%j -H resume"); +#endif /* HPUX */ + break; + + case PRINT_QNX: + string_set(&sDefault.szLpqcommand, "lpq -P%p"); + string_set(&sDefault.szLprmcommand, "lprm -P%p %j"); + string_set(&sDefault.szPrintcommand, "lp -r -P%p %s"); + break; + + case PRINT_SOFTQ: + string_set(&sDefault.szLpqcommand, "qstat -l -d%p"); + string_set(&sDefault.szLprmcommand, + "qstat -s -j%j -c"); + string_set(&sDefault.szPrintcommand, + "lp -d%p -s %s; rm %s"); + string_set(&sDefault.szLppausecommand, + "qstat -s -j%j -h"); + string_set(&sDefault.szLpresumecommand, + "qstat -s -j%j -r"); + break; +#ifdef DEVELOPER + case PRINT_TEST: + case PRINT_VLP: + string_set(&sDefault.szPrintcommand, "vlp print %p %s"); + string_set(&sDefault.szLpqcommand, "vlp lpq %p"); + string_set(&sDefault.szLprmcommand, "vlp lprm %p %j"); + string_set(&sDefault.szLppausecommand, "vlp lppause %p %j"); + string_set(&sDefault.szLpresumecommand, "vlp lpresum %p %j"); + string_set(&sDefault.szQueuepausecommand, "vlp queuepause %p"); + string_set(&sDefault.szQueueresumecommand, "vlp queueresume %p"); + break; +#endif /* DEVELOPER */ + + } +} + + +/*************************************************************************** + Initialise the global parameter structure. +***************************************************************************/ +static void init_globals(void) +{ + pstring s; + int i; + + DEBUG(3, ("Initialising global parameters\n")); + + for (i = 0; parm_table[i].label; i++) { + if ((parm_table[i].type == P_STRING || + parm_table[i].type == P_USTRING) && + parm_table[i].ptr && + !(parm_table[i].flags & FLAG_CMDLINE)) { + string_set(parm_table[i].ptr, ""); + } + } + + /* options that can be set on the command line must be initialised via + the slower do_parameter() to ensure that FLAG_CMDLINE is obeyed */ + do_parameter("socket options", DEFAULT_SOCKET_OPTIONS); + do_parameter("workgroup", DEFAULT_WORKGROUP); + do_parameter("netbios name", get_myname()); + do_parameter("max protocol", "NT1"); + do_parameter("name resolve order", "lmhosts wins host bcast"); + + init_printer_values(); + + string_set(&sDefault.fstype, FSTYPE_STRING); + string_set(&sDefault.ntvfs_handler, "default"); + + 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, "UTF8"); + + /* Use codepage 850 as a default for the dos character set */ + string_set(&Globals.dos_charset, "CP850"); + + /* + * Allow the default PASSWD_CHAT to be overridden in local.h. + */ + string_set(&Globals.szPasswdChat, DEFAULT_PASSWD_CHAT); + + 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_set(&Globals.szServerString, s); + slprintf(s, sizeof(s) - 1, "%d.%d", DEFAULT_MAJOR_VERSION, + DEFAULT_MINOR_VERSION); + string_set(&Globals.szAnnounceVersion, s); + + 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.szPasswordServer, "*"); + + Globals.AlgorithmicRidBase = BASE_RID; + + Globals.bLoadPrinters = True; + Globals.mangled_stack = 50; + /* 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.minprotocol = PROTOCOL_CORE; + Globals.security = SEC_USER; + Globals.paranoid_server_security = True; + Globals.bEncryptPasswords = True; + Globals.bUpdateEncrypt = False; + Globals.bReadRaw = True; + Globals.bWriteRaw = True; + Globals.bReadPrediction = False; + Globals.bReadbmpx = False; + Globals.bNullPasswords = False; + Globals.bObeyPamRestrictions = False; + Globals.bStripDot = False; + Globals.syslog = 1; + Globals.bSyslogOnly = False; + Globals.bTimestampLogs = True; + 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.ReadSize = 16 * 1024; + Globals.lm_announce = 2; /* = Auto: send only if LM clients found */ + Globals.lm_interval = 60; + Globals.stat_cache_size = 50; /* Number of stat translations we'll keep */ + 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.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.bLanmanAuth = True; /* Do use the LanMan hash if it is available */ + Globals.bNTLMAuth = True; /* Do use NTLMv1 if it is available (otherwise NTLMv2) */ + + Globals.map_to_guest = 0; /* By Default, "Never" */ + Globals.min_passwd_length = MINPASSWDLENGTH; /* By Default, 5. */ + Globals.oplock_break_wait_time = 0; /* By Default, 0 msecs. */ + Globals.enhanced_browsing = True; + Globals.iLockSpinCount = 3; /* Try 2 times. */ + Globals.iLockSpinTime = 10; /* usec. */ +#ifdef MMAP_BLACKLIST + Globals.bUseMmap = False; +#else + Globals.bUseMmap = True; +#endif + Globals.bUnixExtensions = False; + + /* 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 guest", NULL); +#else + Globals.szPassdbBackend = str_list_make("smbpasswd guest", NULL); +#endif /* WITH_LDAP_SAMCONFIG */ + + string_set(&Globals.szLdapSuffix, ""); + string_set(&Globals.szLdapMachineSuffix, ""); + string_set(&Globals.szLdapUserSuffix, ""); + + string_set(&Globals.szLdapFilter, "(&(uid=%u)(objectclass=sambaAccount))"); + string_set(&Globals.szLdapAdminDn, ""); + Globals.ldap_ssl = LDAP_SSL_ON; + Globals.ldap_passwd_sync = LDAP_PASSWD_SYNC_OFF; + +/* 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.szWinbindSeparator, "\\"); + string_set(&Globals.szAclCompat, ""); + + Globals.winbind_cache_time = 15; + Globals.bWinbindEnumUsers = True; + Globals.bWinbindEnumGroups = True; + Globals.bWinbindUseDefaultDomain = False; + + string_set(&Globals.szIDMapBackend, "tdb"); + + Globals.name_cache_timeout = 660; /* In seconds */ + + Globals.bUseSpnego = True; + Globals.bClientUseSpnego = True; + + 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) +{ + size_t len = s ? strlen(s) : 0; + char *ret; + + /* The follow debug is useful for tracking down memory problems + especially if you have an inner loop that is calling a lp_*() + function that returns a string. Perhaps this debug should be + present all the time? */ + +#if 0 + DEBUG(10, ("lp_string(%s)\n", s)); +#endif +#if 0 /* until REWRITE done to make thread-safe */ + if (!lp_talloc) + lp_talloc = talloc_init("lp_talloc"); + + ret = (char *)talloc(lp_talloc, len + 100); /* leave room for substitution */ + + if (!ret) + return NULL; + + if (!s) + *ret = 0; + else + StrnCpy(ret, s, len); + + if (trim_string(ret, "\"", "\"")) { + if (strchr(ret,'"') != NULL) + StrnCpy(ret, s, len); + } + + standard_sub_basic(ret,len+100); + return (ret); +#endif + return s; +} + +/* + In this section all the functions that are used to access the + parameters from the rest of the program are defined +*/ + +#define FN_GLOBAL_STRING(fn_name,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_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) +#ifdef WITH_UTMP +FN_GLOBAL_STRING(lp_utmpdir, &Globals.szUtmpDir) +FN_GLOBAL_STRING(lp_wtmpdir, &Globals.szWtmpDir) +FN_GLOBAL_BOOL(lp_utmp, &Globals.bUtmp) +#endif +FN_GLOBAL_STRING(lp_rootdir, &Globals.szRootdir) +FN_GLOBAL_STRING(lp_source_environment, &Globals.szSourceEnv) +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_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_ads_server, &Globals.szADSserver) +FN_GLOBAL_STRING(lp_username_map, &Globals.szUsernameMap) +FN_GLOBAL_STRING(lp_socket_options, &Globals.socket_options) +FN_GLOBAL_STRING(lp_workgroup, &Globals.szWorkgroup) +FN_GLOBAL_STRING(lp_netbios_name, &Globals.szNetbiosName) +FN_GLOBAL_STRING(lp_netbios_scope, &Globals.szNetbiosScope) +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_LIST(lp_sam_backend, &Globals.szSamBackend) +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_homedir, &Globals.szTemplateHomedir) +FN_GLOBAL_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_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_STRING(lp_idmap_backend, &Globals.szIDMapBackend) + +#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_machine_suffix, &Globals.szLdapMachineSuffix) +FN_GLOBAL_STRING(lp_ldap_user_suffix, &Globals.szLdapUserSuffix) +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_trust_ids, &Globals.ldap_trust_ids) +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_readprediction, &Globals.bReadPrediction) +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_strip_dot, &Globals.bStripDot) +FN_GLOBAL_BOOL(lp_encrypted_passwords, &Globals.bEncryptPasswords) +FN_GLOBAL_BOOL(lp_update_encrypted, &Globals.bUpdateEncrypt) +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_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_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_time_offset, &Globals.time_offset) +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_readsize, &Globals.ReadSize) +FN_GLOBAL_INTEGER(lp_deadtime, &Globals.deadtime) +FN_GLOBAL_INTEGER(lp_keepalive, &Globals.keepalive) +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_totalprintjobs, &Globals.iTotalPrintJobs) +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_stat_cache_size, &Globals.stat_cache_size) +FN_GLOBAL_INTEGER(lp_map_to_guest, &Globals.map_to_guest) +FN_GLOBAL_INTEGER(lp_min_passwd_length, &Globals.min_passwd_length) +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_STRING(lp_vfsobj, szVfsObjectFile) +FN_LOCAL_STRING(lp_vfs_options, szVfsOptions) +FN_LOCAL_STRING(lp_vfs_path, szVfsPath) +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_STRING(lp_ntvfs_handler, ntvfs_handler) +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_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_use_sendfile, bUseSendfile) +FN_LOCAL_BOOL(lp_profile_acls, bProfileAcls) +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_BOOL(lp_hide_local_users, &Globals.bHideLocalUsers) +FN_GLOBAL_INTEGER(lp_algorithmic_rid_base, &Globals.AlgorithmicRidBase) +FN_GLOBAL_INTEGER(lp_name_cache_timeout, &Globals.name_cache_timeout) +FN_GLOBAL_BOOL(lp_client_signing, &Globals.client_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_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 const char *get_parametrics(int lookup_service, const char *type, const char *option) +{ + char *vfskey; + struct param_opt *data; + + if (lookup_service >= iNumServices) return NULL; + + data = (lookup_service < 0) ? + Globals.param_opt : ServicePtrs[lookup_service]->param_opt; + + asprintf(&vfskey, "%s:%s", type, option); + strlower(vfskey); + + while (data) { + if (strcmp(data->key, vfskey) == 0) { + free(vfskey); + return data->value; + } + data = data->next; + } + + if (lookup_service >= 0) { + /* 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, vfskey) == 0) { + free(vfskey); + return data->value; + } + data = data->next; + } + } + + free(vfskey); + + return NULL; +} + + +/******************************************************************* +convenience routine to return int parameters. +********************************************************************/ +static int lp_int(const char *s) +{ + + if (!s) { + DEBUG(0,("lp_int(%s): is called with NULL!\n",s)); + return (-1); + } + + return 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 False; + } + + for (i=0; _enum[i].name; i++) { + if (strcasecmp(_enum[i].name,s)==0) + return _enum[i].value; + } + + DEBUG(0,("lp_enum(%s,enum): value is not in enum_list!\n",s)); + return (-1); +} + +/* Return parametric option from a given service. Type is a part of option before ':' */ +/* Parametric option has following syntax: 'Type: option = value' */ +/* Returned value is allocated in 'lp_talloc' context */ + +char *lp_parm_string(int lookup_service, const char *type, const char *option) +{ + const char *value = get_parametrics(lookup_service, type, option); + + if (value) + return lp_string(value); + + return NULL; +} + +/* Return parametric option from a given service. Type is a part of option before ':' */ +/* Parametric option has following syntax: 'Type: option = value' */ +/* Returned value is allocated in 'lp_talloc' context */ + +char **lp_parm_string_list(int lookup_service, const char *type, const char *option, + const char *separator) +{ + const char *value = get_parametrics(lookup_service, type, option); + + if (value) + return str_list_make(value, separator); + + return NULL; +} + +/* Return parametric option from a given service. Type is a part of option before ':' */ +/* Parametric option has following syntax: 'Type: option = value' */ + +int lp_parm_int(int lookup_service, const char *type, const char *option) +{ + const char *value = get_parametrics(lookup_service, type, option); + + if (value) + return lp_int(value); + + return (-1); +} + +/* 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 lookup_service, const char *type, const char *option) +{ + const char *value = get_parametrics(lookup_service, type, option); + + if (value) + return lp_ulong(value); + + return (0); +} + +/* 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 lookup_service, const char *type, const char *option, BOOL default_v) +{ + const char *value = get_parametrics(lookup_service, type, option); + + if (value) + return lp_bool(value); + + return default_v; +} + +/* Return parametric option from a given service. Type is a part of option before ':' */ +/* Parametric option has following syntax: 'Type: option = value' */ + +int lp_parm_enum(int lookup_service, const char *type, const char *option, + const struct enum_list *_enum) +{ + const char *value = get_parametrics(lookup_service, type, option); + + if (value) + return lp_enum(value, _enum); + + return (-1); +} + + +/*************************************************************************** + 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; + struct param_opt *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))); + } + + DEBUG(5,("Freeing parametrics:\n")); + data = pservice->param_opt; + while (data) { + DEBUG(5,("[%s = %s]\n", data->key, data->value)); + string_free(&data->key); + string_free(&data->value); + 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; + struct param_opt *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); + 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(-1))) { + pstrcpy(newHomedir, pszHomedir); + } else { + pstrcpy(newHomedir, lp_pathname(iDefaultService)); + string_sub(newHomedir,"%H", pszHomedir, sizeof(newHomedir)); + } + + 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); + } + ServicePtrs[i]->bAvailable = sDefault.bAvailable; + 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); + 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)); + + update_server_announce_as_printserver(); + + 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); + struct param_opt *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(*(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); + pdata->value = strdup(data->value); + not_added = False; + break; + } + pdata = pdata->next; + } + if (not_added) { + paramo = smb_xmalloc(sizeof(*paramo)); + paramo->key = strdup(data->key); + paramo->value = strdup(data->value); + DLIST_ADD(pserviceDest->param_opt, paramo); + } + data = data->next; + } +} + +/*************************************************************************** +Check a service for consistency. Return False if the service is in any way +incomplete or faulty, else True. +***************************************************************************/ + +static BOOL service_ok(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; + DEBUG(6, ("lp_file_list_changed()\n")); + + while (f) { + pstring n2; + time_t mod_time; + + pstrcpy(n2, f->name); + standard_sub_basic(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); +} + + +/*************************************************************************** + Do the work of sourcing in environment variable/value pairs. +***************************************************************************/ + +static BOOL source_env(char **lines) +{ + char *varval; + size_t len; + int i; + char *p; + + for (i = 0; lines[i]; i++) { + char *line = lines[i]; + + if ((len = strlen(line)) == 0) + continue; + + if (line[len - 1] == '\n') + line[--len] = '\0'; + + if ((varval = malloc(len + 1)) == NULL) { + DEBUG(0, ("source_env: Not enough memory!\n")); + return (False); + } + + DEBUG(4, ("source_env: Adding to environment: %s\n", line)); + strncpy(varval, line, len); + varval[len] = '\0'; + + p = strchr_m(line, (int)'='); + if (p == NULL) { + DEBUG(4, ("source_env: missing '=': %s\n", line)); + continue; + } + + if (putenv(varval)) { + DEBUG(0, ("source_env: Failed to put environment variable %s\n", + varval)); + continue; + } + + *p = '\0'; + p++; + DEBUG(4, ("source_env: getting var %s = %s\n", line, getenv(line))); + } + + DEBUG(4, ("source_env: returning successfully\n")); + return (True); +} + +/*************************************************************************** + Handle the source environment operation. +***************************************************************************/ + +static BOOL handle_source_env(const char *pszParmValue, char **ptr) +{ + pstring fname; + char *p = fname; + BOOL result; + char **lines; + + pstrcpy(fname, pszParmValue); + + standard_sub_basic(fname,sizeof(fname)); + + string_set(ptr, pszParmValue); + + DEBUG(4, ("handle_source_env: checking env type\n")); + + /* + * Filename starting with '|' means popen and read from stdin. + */ + + if (*p == '|') + lines = file_lines_pload(p + 1, NULL); + else + lines = file_lines_load(fname, NULL); + + if (!lines) { + DEBUG(0, ("handle_source_env: Failed to open file %s, Error was %s\n", + fname, strerror(errno))); + return (False); + } + + result = source_env(lines); + file_lines_free(lines); + + return (result); +} + +/*************************************************************************** + Handle the interpretation of the vfs object parameter. +*************************************************************************/ + +static BOOL handle_vfs_object(const char *pszParmValue, char **ptr) +{ + /* Set string value */ + + string_set(ptr, pszParmValue); + + /* Do any other initialisation required for vfs. Note that + anything done here may have linking repercussions in nmbd. */ + + return True; +} + +/*************************************************************************** + Handle the include operation. +***************************************************************************/ + +static BOOL handle_include(const char *pszParmValue, char **ptr) +{ + pstring fname; + pstrcpy(fname, pszParmValue); + + standard_sub_basic(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 winbind/non unix account uid and gid allocation parameters. The format of these + parameters is: + + [global] + + winbind uid = 1000-1999 + winbind gid = 700-899 + + We only do simple parsing checks here. The strings are parsed into useful + structures in the winbind daemon code. + +***************************************************************************/ + +/* Some lp_ routines to return winbind [ug]id information */ + +static uid_t winbind_uid_low, winbind_uid_high; +static gid_t winbind_gid_low, winbind_gid_high; +static uint32 non_unix_account_low, non_unix_account_high; + +BOOL lp_winbind_uid(uid_t *low, uid_t *high) +{ + if (winbind_uid_low == 0 || winbind_uid_high == 0) + return False; + + if (low) + *low = winbind_uid_low; + + if (high) + *high = winbind_uid_high; + + return True; +} + +BOOL lp_winbind_gid(gid_t *low, gid_t *high) +{ + if (winbind_gid_low == 0 || winbind_gid_high == 0) + return False; + + if (low) + *low = winbind_gid_low; + + if (high) + *high = winbind_gid_high; + + return True; +} + +BOOL lp_non_unix_account_range(uint32 *low, uint32 *high) +{ + if (non_unix_account_low == 0 || non_unix_account_high == 0) + return False; + + if (low) + *low = non_unix_account_low; + + if (high) + *high = non_unix_account_high; + + return True; +} + +/* Do some simple checks on "winbind [ug]id" parameter values */ + +static BOOL handle_winbind_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); + + winbind_uid_low = low; + winbind_uid_high = high; + + return True; +} + +static BOOL handle_winbind_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); + + winbind_gid_low = low; + winbind_gid_high = high; + + return True; +} + +/*************************************************************************** + Do some simple checks on "non unix account range" parameter values. +***************************************************************************/ + +static BOOL handle_non_unix_account_range(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); + + non_unix_account_low = low; + non_unix_account_high = high; + + return True; +} + +/*************************************************************************** + Handle the ldap machine suffix option. +***************************************************************************/ + +static BOOL handle_ldap_machine_suffix( const char *pszParmValue, char **ptr) +{ + pstring suffix; + + pstrcpy(suffix, pszParmValue); + + if (! *Globals.szLdapSuffix ) { + string_set( ptr, suffix ); + return True; + } + + if (! strstr(suffix, Globals.szLdapSuffix) ) { + if ( *pszParmValue ) + pstrcat(suffix, ","); + pstrcat(suffix, Globals.szLdapSuffix); + } + string_set( ptr, suffix ); + return True; +} + +/*************************************************************************** + Handle the ldap user suffix option. +***************************************************************************/ + +static BOOL handle_ldap_user_suffix( const char *pszParmValue, char **ptr) +{ + pstring suffix; + + pstrcpy(suffix, pszParmValue); + + if (! *Globals.szLdapSuffix ) { + string_set( ptr, suffix ); + return True; + } + + if (! strstr(suffix, Globals.szLdapSuffix) ) { + if ( *pszParmValue ) + pstrcat(suffix, ","); + pstrcat(suffix, Globals.szLdapSuffix); + } + string_set( ptr, suffix ); + return True; +} + +/*************************************************************************** + Handle setting ldap suffix and determines whether ldap machine suffix needs + to be set as well. +***************************************************************************/ + +static BOOL handle_ldap_suffix( const char *pszParmValue, char **ptr) +{ + pstring suffix; + pstring user_suffix; + pstring machine_suffix; + + pstrcpy(suffix, pszParmValue); + + if (! *Globals.szLdapMachineSuffix ) + string_set(&Globals.szLdapMachineSuffix, suffix); + if (! *Globals.szLdapUserSuffix ) + string_set(&Globals.szLdapUserSuffix, suffix); + + if (! strstr(Globals.szLdapMachineSuffix, suffix)) { + pstrcpy(machine_suffix, Globals.szLdapMachineSuffix); + if ( *Globals.szLdapMachineSuffix ) + pstrcat(machine_suffix, ","); + pstrcat(machine_suffix, suffix); + string_set(&Globals.szLdapMachineSuffix, machine_suffix); + } + + if (! strstr(Globals.szLdapUserSuffix, suffix)) { + pstrcpy(user_suffix, Globals.szLdapUserSuffix); + if ( *Globals.szLdapUserSuffix ) + pstrcat(user_suffix, ","); + pstrcat(user_suffix, suffix); + string_set(&Globals.szLdapUserSuffix, user_suffix); + } + + string_set(ptr, suffix); + + return True; +} + +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 parametric option +***************************************************************************/ +static BOOL lp_do_parameter_parametric(int snum, const char *pszParmName, const char *pszParmValue, int flags) +{ + struct param_opt *paramo, *data; + char *name; + + name = strdup(pszParmName); + if (!name) return False; + + string_sub(name, " ", "", 0); + strlower(name); + + if (snum < 0) { + data = Globals.param_opt; + } else { + data = ServicePtrs[snum]->param_opt; + } + + /* Traverse destination */ + for (paramo=data; paramo; paramo=paramo->next) { + /* If we already have the option set, override it unless + it was a command line option and the new one isn't */ + if (strcmp(paramo->key, name) == 0) { + if ((paramo->flags & FLAG_CMDLINE) && + !(flags & FLAG_CMDLINE)) { + return True; + } + + free(paramo->value); + paramo->value = strdup(pszParmValue); + paramo->flags = flags; + free(name); + return True; + } + } + + paramo = smb_xmalloc(sizeof(*paramo)); + paramo->key = strdup(name); + paramo->value = strdup(pszParmValue); + paramo->flags = flags; + if (snum < 0) { + DLIST_ADD(Globals.param_opt, paramo); + } else { + DLIST_ADD(ServicePtrs[snum]->param_opt, paramo); + } + + free(name); + + return True; +} + +/*************************************************************************** + 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; + void *parm_ptr = NULL; /* where we are going to store the result */ + void *def_ptr = NULL; + + parmnum = map_parameter(pszParmName); + + if (parmnum < 0) { + if (strchr(pszParmName, ':')) { + return lp_do_parameter_parametric(snum, pszParmName, pszParmValue, 0); + } + 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)); + } + + /* if the flag has been set on the command line, then don't allow override, + but don't report an error */ + if (parm_table[parmnum].flags & FLAG_CMDLINE) { + return True; + } + + 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: + *(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(*(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)); +} + + +/* + set a parameter from the commandline - this is called from command line parameter + parsing code. It sets the parameter then marks the parameter as unable to be modified + by smb.conf processing +*/ +BOOL lp_set_cmdline(const char *pszParmName, const char *pszParmValue) +{ + int parmnum = map_parameter(pszParmName); + + if (parmnum < 0 && strchr(pszParmName, ':')) { + /* set a parametric option */ + return lp_do_parameter_parametric(-1, pszParmName, pszParmValue, FLAG_CMDLINE); + } + + /* reset the CMDLINE flag in case this has been called before */ + parm_table[parmnum].flags &= ~FLAG_CMDLINE; + + if (!lp_do_parameter(-2, pszParmName, pszParmValue)) { + return False; + } + + parm_table[parmnum].flags |= FLAG_CMDLINE; + return True; +} + +/*************************************************************************** + 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++) + fprintf(f, "%s%s", *list, + ((*(list+1))?", ":"")); + } + 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_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); +} + +/*************************************************************************** + 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'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_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; + struct param_opt *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; + struct param_opt *data; + + if (pService != &sDefault) + fprintf(f, "\n[%s]\n", pService->szService); + + for (i = 0; parm_table[i].label; i++) + if (parm_table[i].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==-1 gives the globals. + Return NULL when out of parameters. +***************************************************************************/ + +struct parm_struct *lp_next_parameter(int snum, int *i, int allparameters) +{ + if (snum == -1) { + /* 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; + } + } +} + +/*************************************************************************** + Announce ourselves as a print server. +***************************************************************************/ + +void update_server_announce_as_printserver(void) +{ + default_server_announce |= SV_TYPE_PRINTQ_SERVER; +} + +/*************************************************************************** + Have we loaded a services file yet? +***************************************************************************/ + +BOOL lp_loaded(void) +{ + return (bLoaded); +} + +/*************************************************************************** + Unload unused services. +***************************************************************************/ + +void lp_killunused(struct server_context *smb, BOOL (*snumused) (struct server_context *, int)) +{ + int i; + for (i = 0; i < iNumServices; i++) { + if (!VALID(i)) + continue; + + if (!snumused || !snumused(smb, 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_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: + case SEC_DOMAIN: + 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; + } +} + +/*************************************************************************** + 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; + struct param_opt *data; + + pstrcpy(n2, pszFname); + standard_sub_basic(n2,sizeof(n2)); + + add_to_file_list(pszFname, n2); + + bRetval = False; + + DEBUG(0, ("lp_load: refreshing parameters from %s\n", pszFname)); + + bInGlobalSection = True; + bGlobalOnly = global_only; + + init_globals(); + + if (save_defaults) + { + lp_save_defaults(); + } + + if (Globals.param_opt != NULL) { + struct param_opt *next; + for (data=Globals.param_opt; data; data=next) { + next = data->next; + if (data->flags & FLAG_CMDLINE) continue; + free(data->key); + free(data->value); + DLIST_REMOVE(Globals.param_opt, data); + free(data); + } + } + + /* 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); + + 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(); + + 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(-1, "wins server", "127.0.0.1"); + } + + init_iconv(); + + 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; + + + 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(serviceName,sizeof(serviceName)); + if (strequal(serviceName, pszServiceName)) + break; + } + } + + if (iService < 0) + DEBUG(7,("lp_servicenumber: couldn't find %s\n", pszServiceName)); + + 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; + + 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) +{ + char *oldname = lp_servicename(snum); + do_section(new_name); + if (snum >= 0) { + snum = lp_servicenumber(new_name); + if (snum >= 0) + lp_do_parameter(snum, "copy", oldname); + } +} + + +/******************************************************************* + 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; +} + +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; +} + + +/******************************************************************* + 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; +} diff --git a/source4/param/params.c b/source4/param/params.c new file mode 100644 index 0000000000..892e5476cc --- /dev/null +++ b/source4/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: + * + * :== {
} EOF + * + *
:==
{ } + * + *
:== '[' NAME ']' + * + * :== 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 . + */ + +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 + * . False if failed or if a lexical error was + * encountered. + * + * ------------------------------------------------------------------------ ** + */ + { + int c; + int i; + int end; + const char *func = "params.c:Section() -"; + + i = 0; /* is the offset of the next free byte in bufr[] and */ + end = 0; /* 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 */ + /* will be one less than . */ + + 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 . 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/source4/passdb/.cvsignore b/source4/passdb/.cvsignore new file mode 100644 index 0000000000..5f2a5c4cf7 --- /dev/null +++ b/source4/passdb/.cvsignore @@ -0,0 +1,2 @@ +*.po +*.po32 diff --git a/source4/passdb/machine_sid.c b/source4/passdb/machine_sid.c new file mode 100644 index 0000000000..f19b4a0c1d --- /dev/null +++ b/source4/passdb/machine_sid.c @@ -0,0 +1,192 @@ +/* + 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 BOOL pdb_generate_sam_sid(void) +{ + char *fname = NULL; + BOOL is_dc = False; + + if(global_sam_sid==NULL) + if(!(global_sam_sid=(DOM_SID *)malloc(sizeof(DOM_SID)))) + return False; + + 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 (secrets_fetch_domain_sid(lp_netbios_name(), global_sam_sid)) { + DOM_SID domain_sid; + + /* We got our sid. If not a pdc/bdc, we're done. */ + if (!is_dc) + return True; + + 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(), global_sam_sid)) { + DEBUG(0,("pdb_generate_sam_sid: Can't store domain SID as a pdc/bdc.\n")); + return False; + } + return True; + } + + if (!sid_equal(&domain_sid, global_sam_sid)) { + + /* Domain name sid doesn't match global sam sid. Re-store global sam sid as domain sid. */ + + DEBUG(0,("pdb_generate_sam_sid: Mismatched SIDs as a pdc/bdc.\n")); + if (!secrets_store_domain_sid(lp_workgroup(), global_sam_sid)) { + DEBUG(0,("pdb_generate_sam_sid: Can't re-store domain SID as a pdc/bdc.\n")); + return False; + } + return True; + } + + return True; + + } + + /* check for an old MACHINE.SID file for backwards compatibility */ + asprintf(&fname, "%s/MACHINE.SID", lp_private_dir()); + + if (read_sid_from_file(fname, global_sam_sid)) { + /* remember it for future reference and unlink the old MACHINE.SID */ + if (!secrets_store_domain_sid(lp_netbios_name(), global_sam_sid)) { + DEBUG(0,("pdb_generate_sam_sid: Failed to store SID from file-1.\n")); + SAFE_FREE(fname); + return False; + } + unlink(fname); + if (is_dc) { + if (!secrets_store_domain_sid(lp_workgroup(), global_sam_sid)) { + DEBUG(0,("pdb_generate_sam_sid: Failed to store domain SID from file-2.\n")); + SAFE_FREE(fname); + return False; + } + } + + /* Stored the old sid from MACHINE.SID successfully.*/ + SAFE_FREE(fname); + return True; + } + + SAFE_FREE(fname); + + /* we don't have the SID in secrets.tdb, we will need to + generate one and save it */ + generate_random_sid(global_sam_sid); + + if (!secrets_store_domain_sid(lp_netbios_name(), global_sam_sid)) { + DEBUG(0,("pdb_generate_sam_sid: Failed to store generated machine SID-3, nb name=%s.\n", lp_netbios_name())); + return False; + } + if (is_dc) { + if (!secrets_store_domain_sid(lp_workgroup(), global_sam_sid)) { + DEBUG(0,("pdb_generate_sam_sid: Failed to store generated domain SID-4, wg name=%s.\n", lp_workgroup())); + return False; + } + } + + return True; +} + +/* 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 (!pdb_generate_sam_sid()) + global_sam_sid=NULL; + + return global_sam_sid; +} + diff --git a/source4/passdb/passdb.c b/source4/passdb/passdb.c new file mode 100644 index 0000000000..39e2d4cb22 --- /dev/null +++ b/source4/passdb/passdb.c @@ -0,0 +1,1167 @@ +/* + 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_PASSDB + +/* + * This is set on startup - it defines the SID for this + * machine, and therefore the SAM database for which it is + * responsible. + */ + +/************************************************************ + Fill the SAM_ACCOUNT with default values. + ***********************************************************/ + +static 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.uid = user->private.gid = -1; + + 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.unknown_3 = 0x00ffffff; /* don't know */ + 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.unknown_5 = 0x00000000; /* don't know */ + 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; + +} + +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; + } +} + + +/********************************************************************** + Alloc 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; +} + + +/************************************************************* + Alloc 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; +} + + +/************************************************************* + Initialises a struct sam_passwd with sane values. + ************************************************************/ + +NTSTATUS pdb_fill_sam_pw(SAM_ACCOUNT *sam_account, const struct passwd *pwd) +{ + GROUP_MAP map; + + const char *guest_account = lp_guestaccount(); + if (!(guest_account && *guest_account)) { + DEBUG(1, ("NULL guest account!?!?\n")); + return NT_STATUS_UNSUCCESSFUL; + } + + 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, lp_workgroup(), PDB_DEFAULT); + + pdb_set_uid(sam_account, pwd->pw_uid, PDB_SET); + pdb_set_gid(sam_account, pwd->pw_gid, PDB_SET); + + /* 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, becouse the user's SID + may already be in security descriptors etc. + + -- abartlet 11-May-02 + */ + + + /* Ensure this *must* be set right */ + if (strcmp(pwd->pw_name, guest_account) == 0) { + if (!pdb_set_user_sid_from_rid(sam_account, DOMAIN_USER_RID_GUEST, PDB_DEFAULT)) { + return NT_STATUS_UNSUCCESSFUL; + } + if (!pdb_set_group_sid_from_rid(sam_account, DOMAIN_GROUP_RID_GUESTS, PDB_DEFAULT)) { + return NT_STATUS_UNSUCCESSFUL; + } + } else { + + if (!pdb_set_user_sid_from_rid(sam_account, + 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 */ + if(pdb_getgrgid(&map, pwd->pw_gid, MAPPING_WITHOUT_PRIV)) { + if (!pdb_set_group_sid(sam_account,&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(sam_account,pdb_gid_to_group_rid(pwd->pw_gid), PDB_SET)) { + DEBUG(0,("Can't set Group SID\n")); + return NT_STATUS_INVALID_PARAMETER; + } + } + } + + /* 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, lp_netbios_name(), + 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, lp_netbios_name(), + 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, lp_netbios_name(), + 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, lp_netbios_name(), + 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; +} + + +/** + * 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)); +} + + +/************************************************************ + 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; + + 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); +} + +/******************************************************************* + Converts NT user RID to a UNIX uid. + ********************************************************************/ + +static 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; +} + + +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, becouse this is the actual + NT fixed value (1000) */ + + return (rid < BASE_RID); +} + +/******************************************************************* + Decides if a RID is a user or group RID. + ********************************************************************/ + +BOOL 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(DOM_SID *sid, char *name, enum SID_NAME_USE *psid_name_use) +{ + uint32 rid; + SAM_ACCOUNT *sam_account = NULL; + GROUP_MAP map; + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_init("local_lookup_sid"); + if (!mem_ctx) { + DEBUG(0,("local_sid_to_gid: No memory\n")); + return False; + } + if (!sid_peek_check_rid(get_global_sam_sid(), sid, &rid)){ + DEBUG(0,("local_sid_to_gid: sid_peek_check_rid return False! SID: %s\n", + sid_string_talloc(mem_ctx, &map.sid))); + return False; + } + talloc_destroy(mem_ctx); + *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; + } + + /* + * Don't try to convert the rid to a name if + * running in appliance mode + */ + + if (lp_hide_local_users()) + return False; + + if (!NT_STATUS_IS_OK(pdb_init_sam(&sam_account))) { + return False; + } + + /* This now does the 'generic' mapping in pdb_unix */ + /* 'guest' is also handled there */ + if (pdb_getsampwsid(sam_account, sid)) { + 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); + + if (pdb_getgrsid(&map, *sid, MAPPING_WITHOUT_PRIV)) { + 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 (pdb_rid_is_user(rid)) { + uid_t uid; + + DEBUG(5, ("assuming RID %u is a user\n", (unsigned)rid)); + + uid = fallback_pdb_user_rid_to_uid(rid); + slprintf(name, sizeof(fstring)-1, "unix_user.%u", (unsigned int)uid); + + return False; /* Indicates that this user was 'not mapped' */ + } 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); + + *psid_name_use = SID_NAME_ALIAS; + + DEBUG(5,("local_lookup_sid: looking up gid %u %s\n", (unsigned int)gid, + gr ? "succeeded" : "failed" )); + + if(!gr) { + slprintf(name, sizeof(fstring)-1, "unix_group.%u", (unsigned int)gid); + return False; /* Indicates that this group was 'not mapped' */ + } + + fstrcpy( name, gr->gr_name); + + DEBUG(5,("local_lookup_sid: found group %s for rid %u\n", name, + (unsigned int)rid )); + return True; + } +} + +/******************************************************************* + 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; + } + + /* + * Don't lookup local unix users if running in appliance mode + */ + if (lp_hide_local_users()) + return False; + + if (!NT_STATUS_IS_OK(pdb_init_sam(&sam_account))) { + return False; + } + + if (pdb_getsampwnam(sam_account, user)) { + 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, MAPPING_WITHOUT_PRIV)) { + /* 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) + 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, MAPPING_WITHOUT_PRIV)){ + return False; + } + + sid_append_rid( &local_sid, pdb_gid_to_group_rid(grp->gr_gid)); + *psid_name_use = SID_NAME_ALIAS; + } + + sid_copy( psid, &local_sid); + + return True; +} + +/**************************************************************************** + Convert a uid to SID - locally. +****************************************************************************/ + +DOM_SID *local_uid_to_sid(DOM_SID *psid, uid_t uid) +{ + struct passwd *pass; + SAM_ACCOUNT *sam_user = NULL; + fstring str; /* sid string buffer */ + + sid_copy(psid, get_global_sam_sid()); + + if((pass = getpwuid_alloc(uid))) { + + if (NT_STATUS_IS_ERR(pdb_init_sam(&sam_user))) { + passwd_free(&pass); + return NULL; + } + + if (pdb_getsampwnam(sam_user, pass->pw_name)) { + sid_copy(psid, pdb_get_user_sid(sam_user)); + } else { + sid_append_rid(psid, fallback_pdb_uid_to_user_rid(uid)); + } + + DEBUG(10,("local_uid_to_sid: uid %u -> SID (%s) (%s).\n", + (unsigned)uid, sid_to_string( str, psid), + pass->pw_name )); + + passwd_free(&pass); + pdb_free_sam(&sam_user); + + } else { + sid_append_rid(psid, fallback_pdb_uid_to_user_rid(uid)); + + DEBUG(10,("local_uid_to_sid: uid %u -> SID (%s) (unknown user).\n", + (unsigned)uid, sid_to_string( str, psid))); + } + + 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) +{ + fstring str; + SAM_ACCOUNT *sam_user = NULL; + + *name_type = SID_NAME_UNKNOWN; + + if (NT_STATUS_IS_ERR(pdb_init_sam(&sam_user))) + return False; + + if (pdb_getsampwsid(sam_user, psid)) { + + if (!IS_SAM_SET(sam_user,PDB_UID)&&!IS_SAM_CHANGED(sam_user,PDB_UID)) { + pdb_free_sam(&sam_user); + return False; + } + + *puid = pdb_get_uid(sam_user); + + DEBUG(10,("local_sid_to_uid: SID %s -> uid (%u) (%s).\n", sid_to_string( str, psid), + (unsigned int)*puid, pdb_get_username(sam_user))); + pdb_free_sam(&sam_user); + } else { + + DOM_SID dom_sid; + uint32 rid; + GROUP_MAP map; + + pdb_free_sam(&sam_user); + + if (pdb_getgrsid(&map, *psid, MAPPING_WITHOUT_PRIV)) { + DEBUG(3, ("local_sid_to_uid: SID '%s' is a group, not a user... \n", sid_to_string(str, psid))); + /* It's a group, not a user... */ + return False; + } + + sid_copy(&dom_sid, psid); + if (!sid_peek_check_rid(get_global_sam_sid(), psid, &rid)) { + DEBUG(3, ("sid_peek_rid failed - sid '%s' is not in our domain\n", sid_to_string(str, psid))); + return False; + } + + if (!pdb_rid_is_user(rid)) { + DEBUG(3, ("local_sid_to_uid: sid '%s' cannot be mapped to a uid algorithmicly becouse it is a group\n", sid_to_string(str, psid))); + return False; + } + + *puid = fallback_pdb_user_rid_to_uid(rid); + + DEBUG(5,("local_sid_to_uid: SID %s algorithmicly mapped to %ld mapped becouse SID was not found in passdb.\n", + sid_to_string(str, psid), (signed long int)(*puid))); + } + + *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 map; + + sid_copy(psid, get_global_sam_sid()); + + if (pdb_getgrgid(&map, gid, MAPPING_WITHOUT_PRIV)) { + sid_copy(psid, &map.sid); + } + else { + sid_append_rid(psid, pdb_gid_to_group_rid(gid)); + } + + 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) +{ + fstring str; + GROUP_MAP map; + + *name_type = SID_NAME_UNKNOWN; + + /* + * We can only convert to a gid if this is our local + * Domain SID (ie. we are the controling authority). + * + * Or in the Builtin SID too. JFM, 11/30/2001 + */ + + if (pdb_getgrsid(&map, *psid, MAPPING_WITHOUT_PRIV)) { + + /* the SID is in the mapping table but not mapped */ + if (map.gid==(gid_t)-1) + return False; + + *pgid = map.gid; + *name_type = map.sid_name_use; + DEBUG(10,("local_sid_to_gid: mapped SID %s (%s) -> gid (%u).\n", + sid_to_string( str, psid), + map.nt_name, (unsigned int)*pgid)); + + } else { + uint32 rid; + SAM_ACCOUNT *sam_user = NULL; + if (NT_STATUS_IS_ERR(pdb_init_sam(&sam_user))) + return False; + + if (pdb_getsampwsid(sam_user, psid)) { + return False; + pdb_free_sam(&sam_user); + } + + pdb_free_sam(&sam_user); + + if (!sid_peek_check_rid(get_global_sam_sid(), psid, &rid)) { + DEBUG(3, ("sid_peek_rid failed - sid '%s' is not in our domain\n", sid_to_string(str, psid))); + return False; + } + + if (pdb_rid_is_user(rid)) + return False; + + *pgid = pdb_group_rid_to_gid(rid); + *name_type = SID_NAME_ALIAS; + DEBUG(10,("local_sid_to_gid: SID %s -> gid (%u).\n", sid_to_string( str, psid), + (unsigned int)*pgid)); + } + + return True; +} + +/************************************************************* + Change a password entry in the local smbpasswd file. + +It is currently being called by SWAT and by smbpasswd. + + --jerry + *************************************************************/ + +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) +{ + struct passwd *pwd = NULL; + 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); + if(!pdb_getsampwnam(sam_pass, user_name)) { + pdb_free_sam(&sam_pass); + + if (local_flags & LOCAL_ADD_USER) { + pwd = getpwnam_alloc(user_name); + } else if (local_flags & LOCAL_DELETE_USER) { + /* Might not exist in /etc/passwd */ + } else { + slprintf(err_str, err_str_len-1,"Failed to find entry for user %s.\n", user_name); + return False; + } + + if (pwd) { + /* Local user found, so init from this */ + if (!NT_STATUS_IS_OK(pdb_init_sam_pw(&sam_pass, pwd))){ + slprintf(err_str, err_str_len-1, "Failed initialise SAM_ACCOUNT for user %s.\n", user_name); + passwd_free(&pwd); + return False; + } + + passwd_free(&pwd); + } else { + if (!NT_STATUS_IS_OK(pdb_init_sam(&sam_pass))){ + slprintf(err_str, err_str_len-1, "Failed initialise SAM_ACCOUNT for user %s.\n", user_name); + return False; + } + + if (!pdb_set_username(sam_pass, user_name, PDB_CHANGED)) { + slprintf(err_str, err_str_len - 1, "Failed to set username for user %s.\n", user_name); + pdb_free_sam(&sam_pass); + return False; + } + } + } else { + /* 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; +} diff --git a/source4/passdb/pdb_compat.c b/source4/passdb/pdb_compat.c new file mode 100644 index 0000000000..0bd003f1e9 --- /dev/null +++ b/source4/passdb/pdb_compat.c @@ -0,0 +1,115 @@ +/* + 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; + TALLOC_CTX *mem_ctx; + + 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; + mem_ctx = talloc_init("pdb_set_user_sid_from_rid"); + if (!mem_ctx) { + DEBUG(1, ("pdb_set_user_sid_from_rid: No memory\n")); + return False; + } + DEBUG(10, ("pdb_set_user_sid_from_rid:\n\tsetting user sid %s from rid %d\n", + sid_string_talloc(mem_ctx, &u_sid),rid)); + talloc_destroy(mem_ctx); + 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; + TALLOC_CTX *mem_ctx; + + 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; + + mem_ctx = talloc_init("pdb_set_user_sid_from_rid"); + if (!mem_ctx) { + DEBUG(1, ("pdb_set_user_sid_from_rid: No memory\n")); + return False; + } + DEBUG(10, ("pdb_set_group_sid_from_rid:\n\tsetting group sid %s from rid %d\n", + sid_string_talloc(mem_ctx, &g_sid), grid)); + talloc_destroy(mem_ctx); + return True; +} + diff --git a/source4/passdb/pdb_get_set.c b/source4/passdb/pdb_get_set.c new file mode 100644 index 0000000000..27aa5923e4 --- /dev/null +++ b/source4/passdb/pdb_get_set.c @@ -0,0 +1,1123 @@ +/* + 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 becouse + * 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_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(10, ("element %d: SET\n", element)); + ret = PDB_SET; + } + + if (bitmap_query(sampass->private.change_flags, element)) { + DEBUG(10, ("element %d: CHANGED\n", element)); + ret = PDB_CHANGED; + } + + if (ret == PDB_DEFAULT) { + DEBUG(10, ("element %d: DEFAULT\n", element)); + } + + return ret; +} + +uid_t pdb_get_uid (const SAM_ACCOUNT *sampass) +{ + if (sampass) + return (sampass->private.uid); + else + return (-1); +} + +gid_t pdb_get_gid (const SAM_ACCOUNT *sampass) +{ + if (sampass) + return (sampass->private.gid); + else + return (-1); +} + +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); +} + +uint32 pdb_get_unknown_3 (const SAM_ACCOUNT *sampass) +{ + if (sampass) + return (sampass->private.unknown_3); + else + return (-1); +} + +uint32 pdb_get_unknown_5 (const SAM_ACCOUNT *sampass) +{ + if (sampass) + return (sampass->private.unknown_5); + else + return (-1); +} + +uint32 pdb_get_unknown_6 (const SAM_ACCOUNT *sampass) +{ + if (sampass) + return (sampass->private.unknown_6); + else + return (-1); +} + +/********************************************************************* + 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_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_falgs.\n",element)); + return False; + } + DEBUG(10, ("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_falgs.\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_falgs.\n",element)); + return False; + } + DEBUG(10, ("element %d -> now DEFAULT\n", element)); + break; + } + + return True; +} + +BOOL pdb_set_uid (SAM_ACCOUNT *sampass, const uid_t uid, enum pdb_value_state flag) +{ + if (!sampass) + return False; + + DEBUG(10, ("pdb_set_uid: setting uid %d, was %d\n", + (int)uid, (int)sampass->private.uid)); + + sampass->private.uid = uid; + + return pdb_set_init_flags(sampass, PDB_UID, flag); +} + +BOOL pdb_set_gid (SAM_ACCOUNT *sampass, const gid_t gid, enum pdb_value_state flag) +{ + if (!sampass) + return False; + + DEBUG(10, ("pdb_set_gid: setting gid %d, was %d\n", + (int)gid, (int)sampass->private.gid)); + + sampass->private.gid = gid; + + return pdb_set_init_flags(sampass, PDB_GID, flag); +} + +BOOL pdb_set_user_sid (SAM_ACCOUNT *sampass, DOM_SID *u_sid, enum pdb_value_state flag) +{ + TALLOC_CTX *mem_ctx; + if (!sampass || !u_sid) + return False; + + sid_copy(&sampass->private.user_sid, u_sid); + + mem_ctx = talloc_init("pdb_set_group_sid"); + if (!mem_ctx) return False; + DEBUG(10, ("pdb_set_user_sid: setting user sid %s\n", + sid_string_talloc(mem_ctx, &sampass->private.user_sid))); + + talloc_destroy(mem_ctx); + 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, DOM_SID *g_sid, enum pdb_value_state flag) +{ + TALLOC_CTX *mem_ctx; + if (!sampass || !g_sid) + return False; + + sid_copy(&sampass->private.group_sid, g_sid); + + mem_ctx = talloc_init("pdb_set_group_sid"); + if (!mem_ctx) return False; + DEBUG(10, ("pdb_set_group_sid: setting group sid %s\n", + sid_string_talloc(mem_ctx, &sampass->private.group_sid))); + talloc_destroy(mem_ctx); + 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); + + sampass->private.nt_pw = data_blob(pwd, NT_HASH_LEN); + + 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); + + sampass->private.lm_pw = data_blob(pwd, LM_HASH_LEN); + + 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_unknown_3 (SAM_ACCOUNT *sampass, uint32 unkn, enum pdb_value_state flag) +{ + if (!sampass) + return False; + + sampass->private.unknown_3 = unkn; + + return pdb_set_init_flags(sampass, PDB_UNKNOWN3, flag); +} + +BOOL pdb_set_unknown_5 (SAM_ACCOUNT *sampass, uint32 unkn, enum pdb_value_state flag) +{ + if (!sampass) + return False; + + sampass->private.unknown_5 = unkn; + + return pdb_set_init_flags(sampass, PDB_UNKNOWN5, 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); +} + + +/* 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; + + 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)) { + 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; + } + + 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; + + nt_lm_owf_gen (plaintext, new_nt_p16, new_lanman_p16); + + if (!pdb_set_nt_passwd (sampass, new_nt_p16, PDB_CHANGED)) + return False; + + 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; +} diff --git a/source4/passdb/pdb_guest.c b/source4/passdb/pdb_guest.c new file mode 100644 index 0000000000..3f0f06d18d --- /dev/null +++ b/source4/passdb/pdb_guest.c @@ -0,0 +1,123 @@ +/* + * '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 *user, const char *sname) +{ + NTSTATUS nt_status; + struct passwd *pass; + const char *guest_account = lp_guestaccount(); + 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 (!sname) { + DEBUG(0,("invalid name specified")); + return NT_STATUS_UNSUCCESSFUL; + } + + if (!strequal(guest_account, sname)) { + return NT_STATUS_NO_SUCH_USER; + } + + pass = getpwnam_alloc(guest_account); + + nt_status = pdb_fill_sam_pw(user, pass); + + passwd_free(&pass); + return nt_status; +} + + +/*************************************************************************** + Search by rid + **************************************************************************/ + +static NTSTATUS guestsam_getsampwrid (struct pdb_methods *methods, + SAM_ACCOUNT *user, uint32 rid) +{ + NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL; + struct passwd *pass = NULL; + const char *guest_account = lp_guestaccount(); + if (!(guest_account && *guest_account)) { + DEBUG(1, ("NULL guest account!?!?\n")); + return nt_status; + } + + if (!methods) { + DEBUG(0,("invalid methods\n")); + return nt_status; + } + + if (rid == DOMAIN_USER_RID_GUEST) { + pass = getpwnam_alloc(guest_account); + if (!pass) { + DEBUG(1, ("guest account %s does not seem to exist...\n", guest_account)); + return NT_STATUS_NO_SUCH_USER; + } + } else { + return NT_STATUS_NO_SUCH_USER; + } + + nt_status = pdb_fill_sam_pw(user, pass); + passwd_free(&pass); + + return nt_status; +} + +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); +} + +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; + + /* There's not very much to initialise here */ + return NT_STATUS_OK; +} diff --git a/source4/passdb/pdb_interface.c b/source4/passdb/pdb_interface.c new file mode 100644 index 0000000000..48a039b3de --- /dev/null +++ b/source4/passdb/pdb_interface.c @@ -0,0 +1,855 @@ +/* + 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 + +/** List of various built-in passdb modules */ +static const struct { + const char *name; + /* Function to create a member of the pdb_methods list */ + pdb_init_function init; +} builtin_pdb_init_functions[] = { + { "smbpasswd", pdb_init_smbpasswd }, + { "smbpasswd_nua", pdb_init_smbpasswd_nua }, + { "tdbsam", pdb_init_tdbsam }, + { "tdbsam_nua", pdb_init_tdbsam_nua }, + { "ldapsam", pdb_init_ldapsam }, + { "ldapsam_nua", pdb_init_ldapsam_nua }, + { "unixsam", pdb_init_unixsam }, + { "guest", pdb_init_guestsam }, + { "nisplussam", pdb_init_nisplussam }, + { NULL, NULL} +}; + +static struct pdb_init_function_entry *backends; +static void lazy_initialize_passdb(void); + +static void lazy_initialize_passdb() +{ + int i; + static BOOL initialised = False; + + if(!initialised) { + initialised = True; + + for(i = 0; builtin_pdb_init_functions[i].name; i++) { + smb_register_passdb(builtin_pdb_init_functions[i].name, builtin_pdb_init_functions[i].init, PASSDB_INTERFACE_VERSION); + } + } +} + +BOOL smb_register_passdb(const char *name, pdb_init_function init, int version) +{ + struct pdb_init_function_entry *entry = backends; + + if(version != PASSDB_INTERFACE_VERSION) + return False; + + DEBUG(5,("Attempting to register passdb backend %s\n", name)); + + /* Check for duplicates */ + while(entry) { + if(strcasecmp(name, entry->name) == 0) { + DEBUG(0,("There already is a passdb backend registered with the name %s!\n", name)); + return False; + } + entry = entry->next; + } + + entry = smb_xmalloc(sizeof(struct pdb_init_function_entry)); + entry->name = name; + entry->init = init; + + DLIST_ADD(backends, entry); + DEBUG(5,("Successfully added passdb backend '%s'\n", name)); + return True; +} + +struct pdb_init_function_entry *pdb_find_backend_entry(const char *name) +{ + struct pdb_init_function_entry *entry = backends; + + while(entry) { + if (strequal(entry->name, name)) 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; + 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))) { + 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))) { + 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, BOOL with_priv) +{ + 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, with_priv); + 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, BOOL with_priv) +{ + 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, with_priv); + 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, char *name, BOOL with_priv) +{ + 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, with_priv); + 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, BOOL with_priv) +{ + 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, + with_priv); +} + +/****************************************************************** + 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_string(module_location, " ", " "); + } + + trim_string(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) { + smb_probe_module("passdb", module_name); + entry = pdb_find_backend_entry(module_name); + } + + /* No such backend found */ + if(!entry) { + 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)->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; + + if (!NT_STATUS_IS_OK(nt_status = make_pdb_context(context))) { + return nt_status; + } + + while (selected[i]){ + /* 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++; + } + + 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_ERR(make_pdb_context_list(&pdb_context, lp_passdb_backend()))) { + return NULL; + } + } + + if (!pdb_context) { + if (NT_STATUS_IS_ERR(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); + + if (!pdb_context) { + return False; + } + + 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); + + if (!pdb_context) { + return False; + } + + 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, BOOL with_priv) +{ + 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, with_priv)); +} + +BOOL pdb_getgrgid(GROUP_MAP *map, gid_t gid, BOOL with_priv) +{ + 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, with_priv)); +} + +BOOL pdb_getgrnam(GROUP_MAP *map, char *name, BOOL with_priv) +{ + 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, with_priv)); +} + +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, BOOL with_priv) +{ + 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, + with_priv)); +} + +/*************************************************************** + 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; */ +} + +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; + + return NT_STATUS_OK; +} diff --git a/source4/passdb/pdb_ldap.c b/source4/passdb/pdb_ldap.c new file mode 100644 index 0000000000..0136a33871 --- /dev/null +++ b/source4/passdb/pdb_ldap.c @@ -0,0 +1,2089 @@ +/* + Unix SMB/CIFS implementation. + LDAP protocol helper functions for SAMBA + Copyright (C) Jean François Micouleau 1998 + Copyright (C) Gerald Carter 2001 + Copyright (C) Shahms King 2001 + Copyright (C) Andrew Bartlett 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 + +#ifdef HAVE_LDAP +/* 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 +#include + +#ifndef SAM_ACCOUNT +#define SAM_ACCOUNT struct sam_passwd +#endif + +struct ldapsam_privates { + + /* Former statics */ + LDAP *ldap_struct; + LDAPMessage *result; + LDAPMessage *entry; + int index; + + time_t last_ping; + /* retrive-once info */ + const char *uri; + + BOOL permit_non_unix_accounts; + + uint32 low_nua_rid; + uint32 high_nua_rid; + + char *bind_dn; + char *bind_secret; +}; + +#define LDAPSAM_DONT_PING_TIME 10 /* ping only all 10 seconds */ + +static struct ldapsam_privates *static_ldap_state; + +static uint32 ldapsam_get_next_available_nua_rid(struct ldapsam_privates *ldap_state); + +/******************************************************************* + find the ldap password +******************************************************************/ +static BOOL fetch_ldapsam_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_ldapsam_pw: asprintf failed!\n")); + } + + *pw=secrets_fetch(key, &size); + 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_ldapsam_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; + } + + 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; +} + +static const char *attr[] = {"uid", "pwdLastSet", "logonTime", + "logoffTime", "kickoffTime", "cn", + "pwdCanChange", "pwdMustChange", + "displayName", "homeDrive", + "smbHome", "scriptPath", + "profilePath", "description", + "userWorkstations", "rid", + "primaryGroupID", "lmPassword", + "ntPassword", "acctFlags", + "domain", "objectClass", + "uidNumber", "gidNumber", + "homeDirectory", NULL }; + +/******************************************************************* + open a connection to the ldap server. +******************************************************************/ +static int ldapsam_open_connection (struct ldapsam_privates *ldap_state, LDAP ** ldap_struct) +{ + int rc = LDAP_SUCCESS; + int version; + BOOL ldap_v3 = False; + +#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) + DEBUG(10, ("ldapsam_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 ( strncasecmp( p, "URL:", 4 ) == 0 ) { + p += 4; + } + + sscanf(p, "%10[^:]://%254s[^:]:%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,("ldapsam_open_connection: Secure connection not supported by LDAP client libraries!\n")); + return LDAP_OPERATIONS_ERROR; +#endif + } + } +#endif + + 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,("ldapsam_open_connection: StartTLS not supported by LDAP client libraries!\n")); + return LDAP_OPERATIONS_ERROR; +#endif + } + + DEBUG(2, ("ldapsam_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 ldapsam_privates *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; + } + 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 ldapsam_privates *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); + + 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 ) +{ + return rebindproc_with_state(ldap_struct, whop, credp, + method, freeit, static_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) +{ + return rebindproc_connect_with_state(ld, url, (ber_tag_t)request, msgid, + static_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 ldapsam_connect_system(struct ldapsam_privates *ldap_state, LDAP * ldap_struct) +{ + int rc; + char *ldap_dn; + char *ldap_secret; + + /* The rebind proc needs this *HACK*. We are not multithreaded, so + this will work, but it's not nice. */ + static_ldap_state = ldap_state; + + /* get the password */ + if (!fetch_ldapsam_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; + ldap_get_option(ldap_state->ldap_struct, LDAP_OPT_ERROR_STRING, + &ld_error); + DEBUG(0, + ("failed to bind to server with dn= %s Error: %s\n\t%s\n", + ldap_dn, ldap_err2string(rc), + ld_error)); + free(ld_error); + return rc; + } + + DEBUG(2, ("ldap_connect_system: succesful connection to the LDAP server\n")); + return rc; +} + +/********************************************************************** +Connect to LDAP server +*********************************************************************/ +static int ldapsam_open(struct ldapsam_privates *ldap_state) +{ + int rc; + SMB_ASSERT(ldap_state); + +#ifndef NO_LDAP_SECURITY + if (geteuid() != 0) { + DEBUG(0, ("ldapsam_open: cannot access LDAP when not root..\n")); + return LDAP_INSUFFICIENT_ACCESS; + } +#endif + + if ((ldap_state->ldap_struct != NULL) && ((ldap_state->last_ping + LDAPSAM_DONT_PING_TIME) < time(NULL))) { + struct sockaddr_un addr; + socklen_t len; + 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(5,("ldapsam_open: allready connected to the LDAP server\n")); + return LDAP_SUCCESS; + } + + if ((rc = ldapsam_open_connection(ldap_state, &ldap_state->ldap_struct))) { + return rc; + } + + if ((rc = ldapsam_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 ldapsam_close(struct ldapsam_privates *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; + } + + DEBUG(5,("The connection to the LDAP server was closed\n")); + /* maybe free the results here --metze */ + + return NT_STATUS_OK; +} + +static int ldapsam_retry_open(struct ldapsam_privates *ldap_state, int *attempts) +{ + int rc; + + SMB_ASSERT(ldap_state && attempts); + + if (*attempts != 0) { + /* we retry after 0.5, 2, 4.5, 8, 12.5, 18, 24.5 seconds */ + msleep((((*attempts)*(*attempts))/2)*1000); + } + (*attempts)++; + + if ((rc = ldapsam_open(ldap_state))) { + DEBUG(0,("Connection to LDAP Server failed for the %d try!\n",*attempts)); + return rc; + } + + return LDAP_SUCCESS; +} + + +static int ldapsam_search(struct ldapsam_privates *ldap_state, + const char *base, int scope, const char *filter, + const char *attrs[], int attrsonly, + LDAPMessage **res) +{ + int rc = LDAP_SERVER_DOWN; + int attempts = 0; + + SMB_ASSERT(ldap_state); + + while ((rc == LDAP_SERVER_DOWN) && (attempts < 8)) { + + if ((rc = ldapsam_retry_open(ldap_state,&attempts)) != LDAP_SUCCESS) + continue; + + rc = ldap_search_s(ldap_state->ldap_struct, base, scope, + filter, attrs, attrsonly, res); + } + + if (rc == LDAP_SERVER_DOWN) { + DEBUG(0,("ldapsam_seacrh: LDAP server is down!\n")); + ldapsam_close(ldap_state); + } + + return rc; +} + +static int ldapsam_modify(struct ldapsam_privates *ldap_state, char *dn, LDAPMod *attrs[]) +{ + int rc = LDAP_SERVER_DOWN; + int attempts = 0; + + if (!ldap_state) + return (-1); + + while ((rc == LDAP_SERVER_DOWN) && (attempts < 8)) { + + if ((rc = ldapsam_retry_open(ldap_state,&attempts)) != LDAP_SUCCESS) + continue; + + rc = ldap_modify_s(ldap_state->ldap_struct, dn, attrs); + } + + if (rc == LDAP_SERVER_DOWN) { + DEBUG(0,("ldapsam_modify: LDAP server is down!\n")); + ldapsam_close(ldap_state); + } + + return rc; +} + +static int ldapsam_add(struct ldapsam_privates *ldap_state, const char *dn, LDAPMod *attrs[]) +{ + int rc = LDAP_SERVER_DOWN; + int attempts = 0; + + if (!ldap_state) + return (-1); + + while ((rc == LDAP_SERVER_DOWN) && (attempts < 8)) { + + if ((rc = ldapsam_retry_open(ldap_state,&attempts)) != LDAP_SUCCESS) + continue; + + rc = ldap_add_s(ldap_state->ldap_struct, dn, attrs); + } + + if (rc == LDAP_SERVER_DOWN) { + DEBUG(0,("ldapsam_add: LDAP server is down!\n")); + ldapsam_close(ldap_state); + } + + return rc; +} + +static int ldapsam_delete(struct ldapsam_privates *ldap_state, char *dn) +{ + int rc = LDAP_SERVER_DOWN; + int attempts = 0; + + if (!ldap_state) + return (-1); + + while ((rc == LDAP_SERVER_DOWN) && (attempts < 8)) { + + if ((rc = ldapsam_retry_open(ldap_state,&attempts)) != LDAP_SUCCESS) + continue; + + rc = ldap_delete_s(ldap_state->ldap_struct, dn); + } + + if (rc == LDAP_SERVER_DOWN) { + DEBUG(0,("ldapsam_delete: LDAP server is down!\n")); + ldapsam_close(ldap_state); + } + + return rc; +} + +static int ldapsam_extended_operation(struct ldapsam_privates *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 < 8)) { + + if ((rc = ldapsam_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,("ldapsam_extended_operation: LDAP server is down!\n")); + ldapsam_close(ldap_state); + } + + return rc; +} + +/******************************************************************* + run the search by name. +******************************************************************/ +static int ldapsam_search_one_user (struct ldapsam_privates *ldap_state, const char *filter, LDAPMessage ** result) +{ + int scope = LDAP_SCOPE_SUBTREE; + int rc; + + DEBUG(2, ("ldapsam_search_one_user: searching for:[%s]\n", filter)); + + rc = ldapsam_search(ldap_state, lp_ldap_suffix (), scope, filter, attr, 0, result); + + if (rc != LDAP_SUCCESS) { + DEBUG(0,("ldapsam_search_one_user: Problem during the LDAP search: %s\n", + ldap_err2string (rc))); + DEBUG(3,("ldapsam_search_one_user: Query was: %s, %s\n", lp_ldap_suffix(), + filter)); + } + + return rc; +} + +/******************************************************************* + run the search by name. +******************************************************************/ +static int ldapsam_search_one_user_by_name (struct ldapsam_privates *ldap_state, const char *user, + LDAPMessage ** result) +{ + 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 :-) + */ + pstrcpy(filter, lp_ldap_filter()); + + /* + * 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 ldapsam_search_one_user(ldap_state, filter, result); +} + +/******************************************************************* + run the search by uid. +******************************************************************/ +static int ldapsam_search_one_user_by_uid(struct ldapsam_privates *ldap_state, + int uid, + LDAPMessage ** result) +{ + struct passwd *user; + pstring filter; + char *escape_user; + + /* Get the username from the system and look that up in the LDAP */ + + if ((user = getpwuid_alloc(uid)) == NULL) { + DEBUG(3,("ldapsam_search_one_user_by_uid: Failed to locate uid [%d]\n", uid)); + return LDAP_NO_SUCH_OBJECT; + } + + pstrcpy(filter, lp_ldap_filter()); + + escape_user = escape_ldap_string_alloc(user->pw_name); + if (!escape_user) { + passwd_free(&user); + return LDAP_NO_MEMORY; + } + + all_string_sub(filter, "%u", escape_user, sizeof(pstring)); + + passwd_free(&user); + SAFE_FREE(escape_user); + + return ldapsam_search_one_user(ldap_state, filter, result); +} + +/******************************************************************* + run the search by rid. +******************************************************************/ +static int ldapsam_search_one_user_by_rid (struct ldapsam_privates *ldap_state, + uint32 rid, + LDAPMessage ** result) +{ + pstring filter; + int rc; + + /* check if the user rid exsists, if not, try searching on the uid */ + + snprintf(filter, sizeof(filter) - 1, "rid=%i", rid); + rc = ldapsam_search_one_user(ldap_state, filter, result); + + if (rc != LDAP_SUCCESS) + rc = ldapsam_search_one_user_by_uid(ldap_state, + fallback_pdb_user_rid_to_uid(rid), + result); + + return rc; +} + +/******************************************************************* +search an attribute and return the first value found. +******************************************************************/ +static BOOL get_single_attribute (LDAP * ldap_struct, LDAPMessage * entry, + const char *attribute, pstring value) +{ + char **values; + + if ((values = ldap_get_values (ldap_struct, entry, attribute)) == NULL) { + value = NULL; + DEBUG (10, ("get_single_attribute: [%s] = []\n", attribute)); + + return False; + } + + pstrcpy(value, values[0]); + ldap_value_free(values); +#ifdef DEBUG_PASSWORDS + DEBUG (100, ("get_single_attribute: [%s] = [%s]\n", attribute, value)); +#endif + return True; +} + +/************************************************************************ +Routine to manage the LDAPMod structure array +manage memory used by the array, by each struct, and values + +************************************************************************/ +static void make_a_mod (LDAPMod *** modlist, int modop, const char *attribute, const char *value) +{ + LDAPMod **mods; + int i; + int j; + + mods = *modlist; + + if (attribute == NULL || *attribute == '\0') + return; + + if (value == NULL || *value == '\0') + return; + + 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 && !strcasecmp(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) + { + 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; + } + mods[i]->mod_values[j] = strdup(value); + mods[i]->mod_values[j + 1] = NULL; + } + *modlist = mods; +} + +/* New Interface is being implemented here */ + +/********************************************************************** +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) +{ + pstring homedir; + pstring temp; + uid_t uid; + gid_t gid; + char **ldap_values; + char **values; + + if ((ldap_values = ldap_get_values (ldap_state->ldap_struct, entry, "objectClass")) == NULL) { + DEBUG (1, ("get_unix_attributes: no objectClass! \n")); + return False; + } + + for (values=ldap_values;*values;values++) { + if (strcasecmp(*values, "posixAccount") == 0) { + break; + } + } + + if (!*values) { /*end of array, no posixAccount */ + DEBUG(10, ("user does not have posixAcccount attributes\n")); + ldap_value_free(ldap_values); + return False; + } + ldap_value_free(ldap_values); + + if (!get_single_attribute(ldap_state->ldap_struct, entry, "homeDirectory", homedir)) + return False; + + if (!get_single_attribute(ldap_state->ldap_struct, entry, "uidNumber", temp)) + return False; + + uid = (uid_t)atol(temp); + + if (!get_single_attribute(ldap_state->ldap_struct, entry, "gidNumber", temp)) + return False; + + gid = (gid_t)atol(temp); + + pdb_set_unix_homedir(sampass, homedir, PDB_SET); + pdb_set_uid(sampass, uid, PDB_SET); + pdb_set_gid(sampass, gid, PDB_SET); + + DEBUG(10, ("user has posixAcccount attributes\n")); + return True; +} + + +/********************************************************************** +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; + pstring username, + domain, + nt_username, + fullname, + homedir, + dir_drive, + logon_script, + profile_path, + acct_desc, + munged_dial, + workstations; + struct passwd *pw; + uint32 user_rid, + group_rid; + uint8 smblmpwd[LM_HASH_LEN], + smbntpwd[NT_HASH_LEN]; + uint16 acct_ctrl = 0, + logon_divs; + uint32 hours_len; + uint8 hours[MAX_HOURS_LEN]; + pstring temp; + uid_t uid = -1; + gid_t gid = getegid(); + + + /* + * 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->ldap_struct == NULL) { + DEBUG(0, ("init_sam_from_ldap: ldap_state->ldap_struct is NULL!\n")); + return False; + } + + get_single_attribute(ldap_state->ldap_struct, entry, "uid", username); + DEBUG(2, ("Entry found for user: %s\n", username)); + + pstrcpy(nt_username, username); + + pstrcpy(domain, lp_workgroup()); + + pdb_set_username(sampass, username, PDB_SET); + + pdb_set_domain(sampass, domain, PDB_DEFAULT); + pdb_set_nt_username(sampass, nt_username, PDB_SET); + + get_single_attribute(ldap_state->ldap_struct, entry, "rid", temp); + user_rid = (uint32)atol(temp); + + pdb_set_user_sid_from_rid(sampass, user_rid, PDB_SET); + + if (!get_single_attribute(ldap_state->ldap_struct, entry, "primaryGroupID", temp)) { + group_rid = 0; + } else { + group_rid = (uint32)atol(temp); + pdb_set_group_sid_from_rid(sampass, group_rid, PDB_SET); + } + + + /* + * If so configured, try and get the values from LDAP + */ + + if (!lp_ldap_trust_ids() || (!get_unix_attributes(ldap_state, sampass, entry))) { + + /* + * Otherwise just ask the system getpw() calls. + */ + + pw = getpwnam_alloc(username); + if (pw == NULL) { + if (! ldap_state->permit_non_unix_accounts) { + DEBUG (2,("init_sam_from_ldap: User [%s] does not exist via system getpwnam!\n", username)); + return False; + } + } else { + uid = pw->pw_uid; + pdb_set_uid(sampass, uid, PDB_SET); + gid = pw->pw_gid; + pdb_set_gid(sampass, gid, PDB_SET); + + pdb_set_unix_homedir(sampass, pw->pw_dir, PDB_SET); + + passwd_free(&pw); + } + } + + if (group_rid == 0 && pdb_get_init_flags(sampass,PDB_GID) != PDB_DEFAULT) { + GROUP_MAP map; + gid = pdb_get_gid(sampass); + /* call the mapping code here */ + if(pdb_getgrgid(&map, gid, MAPPING_WITHOUT_PRIV)) { + pdb_set_group_sid(sampass, &map.sid, PDB_SET); + } + else { + pdb_set_group_sid_from_rid(sampass, pdb_gid_to_group_rid(gid), PDB_SET); + } + } + + if (!get_single_attribute(ldap_state->ldap_struct, entry, "pwdLastSet", 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 (!get_single_attribute(ldap_state->ldap_struct, entry, "logonTime", temp)) { + /* leave as default */ + } else { + logon_time = (time_t) atol(temp); + pdb_set_logon_time(sampass, logon_time, PDB_SET); + } + + if (!get_single_attribute(ldap_state->ldap_struct, entry, "logoffTime", temp)) { + /* leave as default */ + } else { + logoff_time = (time_t) atol(temp); + pdb_set_logoff_time(sampass, logoff_time, PDB_SET); + } + + if (!get_single_attribute(ldap_state->ldap_struct, entry, "kickoffTime", temp)) { + /* leave as default */ + } else { + kickoff_time = (time_t) atol(temp); + pdb_set_kickoff_time(sampass, kickoff_time, PDB_SET); + } + + if (!get_single_attribute(ldap_state->ldap_struct, entry, "pwdCanChange", 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 (!get_single_attribute(ldap_state->ldap_struct, entry, "pwdMustChange", 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 (!get_single_attribute(ldap_state->ldap_struct, entry, "cn", fullname)) { + if (!get_single_attribute(ldap_state->ldap_struct, entry, "displayName", fullname)) { + /* leave as default */ + } else { + pdb_set_fullname(sampass, fullname, PDB_SET); + } + } else { + pdb_set_fullname(sampass, fullname, PDB_SET); + } + + if (!get_single_attribute(ldap_state->ldap_struct, entry, "homeDrive", dir_drive)) { + pdb_set_dir_drive(sampass, talloc_sub_specified(sampass->mem_ctx, + lp_logon_drive(), + username, domain, + uid, gid), + PDB_DEFAULT); + } else { + pdb_set_dir_drive(sampass, dir_drive, PDB_SET); + } + + if (!get_single_attribute(ldap_state->ldap_struct, entry, "smbHome", homedir)) { + pdb_set_homedir(sampass, talloc_sub_specified(sampass->mem_ctx, + lp_logon_home(), + username, domain, + uid, gid), + PDB_DEFAULT); + } else { + pdb_set_homedir(sampass, homedir, PDB_SET); + } + + if (!get_single_attribute(ldap_state->ldap_struct, entry, "scriptPath", logon_script)) { + pdb_set_logon_script(sampass, talloc_sub_specified(sampass->mem_ctx, + lp_logon_script(), + username, domain, + uid, gid), + PDB_DEFAULT); + } else { + pdb_set_logon_script(sampass, logon_script, PDB_SET); + } + + if (!get_single_attribute(ldap_state->ldap_struct, entry, "profilePath", profile_path)) { + pdb_set_profile_path(sampass, talloc_sub_specified(sampass->mem_ctx, + lp_logon_path(), + username, domain, + uid, gid), + PDB_DEFAULT); + } else { + pdb_set_profile_path(sampass, profile_path, PDB_SET); + } + + if (!get_single_attribute(ldap_state->ldap_struct, entry, "description", acct_desc)) { + /* leave as default */ + } else { + pdb_set_acct_desc(sampass, acct_desc, PDB_SET); + } + + if (!get_single_attribute(ldap_state->ldap_struct, entry, "userWorkstations", workstations)) { + /* leave as default */; + } else { + pdb_set_workstations(sampass, workstations, PDB_SET); + } + + /* FIXME: hours stuff should be cleaner */ + + logon_divs = 168; + hours_len = 21; + memset(hours, 0xff, hours_len); + + if (!get_single_attribute (ldap_state->ldap_struct, entry, "lmPassword", 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 (!get_single_attribute (ldap_state->ldap_struct, entry, "ntPassword", 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 (!get_single_attribute (ldap_state->ldap_struct, entry, "acctFlags", 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); + + /* pdb_set_unknown_3(sampass, unknown3, PDB_SET); */ + /* pdb_set_unknown_5(sampass, unknown5, PDB_SET); */ + /* pdb_set_unknown_6(sampass, unknown6, PDB_SET); */ + + pdb_set_hours(sampass, hours, PDB_SET); + + return True; +} + +static BOOL need_ldap_mod(BOOL pdb_add, const SAM_ACCOUNT * sampass, enum pdb_elements element) { + if (pdb_add) { + return (!IS_SAM_DEFAULT(sampass, element)); + } else { + return IS_SAM_CHANGED(sampass, element); + } +} + +/********************************************************************** +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, + LDAPMod *** mods, int ldap_op, + BOOL pdb_add, + const SAM_ACCOUNT * sampass) +{ + 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_ldap_mod(pdb_add, sampass, PDB_USERNAME)) { + make_a_mod(mods, ldap_op, "uid", pdb_get_username(sampass)); + DEBUG(2, ("Setting entry for user: %s\n", pdb_get_username(sampass))); + } + + if ((rid = pdb_get_user_rid(sampass))!=0 ) { + if (need_ldap_mod(pdb_add, sampass, PDB_USERSID)) { + slprintf(temp, sizeof(temp) - 1, "%i", rid); + make_a_mod(mods, ldap_op, "rid", temp); + } + } else if (!IS_SAM_DEFAULT(sampass, PDB_UID)) { + rid = fallback_pdb_uid_to_user_rid(pdb_get_uid(sampass)); + slprintf(temp, sizeof(temp) - 1, "%i", rid); + make_a_mod(mods, ldap_op, "rid", temp); + } else if (ldap_state->permit_non_unix_accounts) { + rid = ldapsam_get_next_available_nua_rid(ldap_state); + if (rid == 0) { + DEBUG(0, ("NO user RID specified on account %s, and findining next available NUA RID failed, cannot store!\n", pdb_get_username(sampass))); + return False; + } + slprintf(temp, sizeof(temp) - 1, "%i", rid); + make_a_mod(mods, ldap_op, "rid", temp); + } else { + DEBUG(0, ("NO user RID specified on account %s, cannot store!\n", pdb_get_username(sampass))); + return False; + } + + + + if ((rid = pdb_get_group_rid(sampass))!=0 ) { + if (need_ldap_mod(pdb_add, sampass, PDB_GROUPSID)) { + slprintf(temp, sizeof(temp) - 1, "%i", rid); + make_a_mod(mods, ldap_op, "primaryGroupID", temp); + } + } else if (!IS_SAM_DEFAULT(sampass, PDB_GID)) { + rid = pdb_gid_to_group_rid(pdb_get_gid(sampass)); + slprintf(temp, sizeof(temp) - 1, "%i", rid); + make_a_mod(mods, ldap_op, "primaryGroupID", temp); + } else if (ldap_state->permit_non_unix_accounts) { + rid = DOMAIN_GROUP_RID_USERS; + slprintf(temp, sizeof(temp) - 1, "%i", rid); + make_a_mod(mods, ldap_op, "primaryGroupID", temp); + } else { + DEBUG(0, ("NO group RID specified on account %s, cannot store!\n", pdb_get_username(sampass))); + return False; + } + + + /* 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 + */ + if (need_ldap_mod(pdb_add, sampass, PDB_FULLNAME)) { + make_a_mod(mods, ldap_op, "displayName", pdb_get_fullname(sampass)); + make_a_mod(mods, ldap_op, "cn", pdb_get_fullname(sampass)); + } + if (need_ldap_mod(pdb_add, sampass, PDB_ACCTDESC)) { + make_a_mod(mods, ldap_op, "description", pdb_get_acct_desc(sampass)); + } + if (need_ldap_mod(pdb_add, sampass, PDB_WORKSTATIONS)) { + make_a_mod(mods, ldap_op, "userWorkstations", pdb_get_workstations(sampass)); + } + /* + * Only updates fields which have been set (not defaults from smb.conf) + */ + + if (need_ldap_mod(pdb_add, sampass, PDB_SMBHOME)) { + make_a_mod(mods, ldap_op, "smbHome", pdb_get_homedir(sampass)); + } + + if (need_ldap_mod(pdb_add, sampass, PDB_DRIVE)) { + make_a_mod(mods, ldap_op, "homeDrive", pdb_get_dir_drive(sampass)); + } + + if (need_ldap_mod(pdb_add, sampass, PDB_LOGONSCRIPT)) { + make_a_mod(mods, ldap_op, "scriptPath", pdb_get_logon_script(sampass)); + } + + if (need_ldap_mod(pdb_add, sampass, PDB_PROFILE)) + make_a_mod(mods, ldap_op, "profilePath", pdb_get_profile_path(sampass)); + + if (need_ldap_mod(pdb_add, sampass, PDB_LOGONTIME)) { + slprintf(temp, sizeof(temp) - 1, "%li", pdb_get_logon_time(sampass)); + make_a_mod(mods, ldap_op, "logonTime", temp); + } + + if (need_ldap_mod(pdb_add, sampass, PDB_LOGOFFTIME)) { + slprintf(temp, sizeof(temp) - 1, "%li", pdb_get_logoff_time(sampass)); + make_a_mod(mods, ldap_op, "logoffTime", temp); + } + + if (need_ldap_mod(pdb_add, sampass, PDB_KICKOFFTIME)) { + slprintf (temp, sizeof (temp) - 1, "%li", pdb_get_kickoff_time(sampass)); + make_a_mod(mods, ldap_op, "kickoffTime", temp); + } + + + if (need_ldap_mod(pdb_add, sampass, PDB_CANCHANGETIME)) { + slprintf (temp, sizeof (temp) - 1, "%li", pdb_get_pass_can_change_time(sampass)); + make_a_mod(mods, ldap_op, "pwdCanChange", temp); + } + + if (need_ldap_mod(pdb_add, sampass, PDB_MUSTCHANGETIME)) { + slprintf (temp, sizeof (temp) - 1, "%li", pdb_get_pass_must_change_time(sampass)); + make_a_mod(mods, ldap_op, "pwdMustChange", temp); + } + + if ((pdb_get_acct_ctrl(sampass)&(ACB_WSTRUST|ACB_SVRTRUST|ACB_DOMTRUST))|| + (lp_ldap_passwd_sync()!=LDAP_PASSWD_SYNC_ONLY)) { + + if (need_ldap_mod(pdb_add, sampass, PDB_LMPASSWD)) { + pdb_sethexpwd (temp, pdb_get_lanman_passwd(sampass), pdb_get_acct_ctrl(sampass)); + make_a_mod (mods, ldap_op, "lmPassword", temp); + } + + if (need_ldap_mod(pdb_add, sampass, PDB_NTPASSWD)) { + pdb_sethexpwd (temp, pdb_get_nt_passwd(sampass), pdb_get_acct_ctrl(sampass)); + make_a_mod (mods, ldap_op, "ntPassword", temp); + } + + if (need_ldap_mod(pdb_add, sampass, PDB_PASSLASTSET)) { + slprintf (temp, sizeof (temp) - 1, "%li", pdb_get_pass_last_set_time(sampass)); + make_a_mod(mods, ldap_op, "pwdLastSet", temp); + } + } + + /* FIXME: Hours stuff goes in LDAP */ + if (need_ldap_mod(pdb_add, sampass, PDB_ACCTCTRL)) { + make_a_mod (mods, ldap_op, "acctFlags", pdb_encode_acct_ctrl (pdb_get_acct_ctrl(sampass), + NEW_PW_FORMAT_SPACE_PADDED_LEN)); + } + + return True; +} + + +/********************************************************************** +Connect to LDAP server and find the next available RID. +*********************************************************************/ +static uint32 check_nua_rid_is_avail(struct ldapsam_privates *ldap_state, uint32 top_rid) +{ + LDAPMessage *result; + uint32 final_rid = (top_rid & (~USER_RID_TYPE)) + RID_MULTIPLIER; + if (top_rid == 0) { + return 0; + } + + if (final_rid < ldap_state->low_nua_rid || final_rid > ldap_state->high_nua_rid) { + return 0; + } + + if (ldapsam_search_one_user_by_rid(ldap_state, final_rid, &result) != LDAP_SUCCESS) { + DEBUG(0, ("Cannot allocate NUA RID %d (0x%x), as the confirmation search failed!\n", final_rid, final_rid)); + return 0; + } + + if (ldap_count_entries(ldap_state->ldap_struct, result) != 0) { + DEBUG(0, ("Cannot allocate NUA RID %d (0x%x), as the RID is already in use!!\n", final_rid, final_rid)); + ldap_msgfree(result); + return 0; + } + + DEBUG(5, ("NUA RID %d (0x%x), declared valid\n", final_rid, final_rid)); + ldap_msgfree(result); + return final_rid; +} + +/********************************************************************** +Extract the RID from an LDAP entry +*********************************************************************/ +static uint32 entry_to_user_rid(struct ldapsam_privates *ldap_state, LDAPMessage *entry) { + uint32 rid; + SAM_ACCOUNT *user = NULL; + if (!NT_STATUS_IS_OK(pdb_init_sam(&user))) { + return 0; + } + + if (init_sam_from_ldap(ldap_state, user, entry)) { + rid = pdb_get_user_rid(user); + } else { + rid =0; + } + pdb_free_sam(&user); + if (rid >= ldap_state->low_nua_rid && rid <= ldap_state->high_nua_rid) { + return rid; + } + return 0; +} + + +/********************************************************************** +Connect to LDAP server and find the next available RID. +*********************************************************************/ +static uint32 search_top_nua_rid(struct ldapsam_privates *ldap_state) +{ + int rc; + pstring filter; + LDAPMessage *result; + LDAPMessage *entry; + char *final_filter = NULL; + uint32 top_rid = 0; + uint32 count; + uint32 rid; + + pstrcpy(filter, lp_ldap_filter()); + all_string_sub(filter, "%u", "*", sizeof(pstring)); + +#if 0 + asprintf(&final_filter, "(&(%s)(&(rid>=%d)(rid<=%d)))", filter, ldap_state->low_nua_rid, ldap_state->high_nua_rid); +#else + final_filter = strdup(filter); +#endif + DEBUG(2, ("ldapsam_get_next_available_nua_rid: searching for:[%s]\n", final_filter)); + + rc = ldapsam_search(ldap_state, lp_ldap_suffix(), + LDAP_SCOPE_SUBTREE, final_filter, attr, 0, + &result); + + if (rc != LDAP_SUCCESS) { + DEBUG(3, ("LDAP search failed! cannot find base for NUA RIDs: %s\n", ldap_err2string(rc))); + DEBUGADD(3, ("Query was: %s, %s\n", lp_ldap_suffix(), final_filter)); + + free(final_filter); + result = NULL; + return 0; + } + + count = ldap_count_entries(ldap_state->ldap_struct, result); + DEBUG(2, ("search_top_nua_rid: %d entries in the base!\n", count)); + + if (count == 0) { + DEBUG(3, ("LDAP search returned no records, assuming no non-unix-accounts present!: %s\n", ldap_err2string(rc))); + DEBUGADD(3, ("Query was: %s, %s\n", lp_ldap_suffix(), final_filter)); + free(final_filter); + ldap_msgfree(result); + result = NULL; + return ldap_state->low_nua_rid; + } + + free(final_filter); + entry = ldap_first_entry(ldap_state->ldap_struct,result); + + top_rid = entry_to_user_rid(ldap_state, entry); + + while ((entry = ldap_next_entry(ldap_state->ldap_struct, entry))) { + + rid = entry_to_user_rid(ldap_state, entry); + if (rid > top_rid) { + top_rid = rid; + } + } + + ldap_msgfree(result); + + if (top_rid < ldap_state->low_nua_rid) + top_rid = ldap_state->low_nua_rid; + + return top_rid; +} + +/********************************************************************** +Connect to LDAP server and find the next available RID. +*********************************************************************/ +static uint32 ldapsam_get_next_available_nua_rid(struct ldapsam_privates *ldap_state) { + uint32 next_nua_rid; + uint32 top_nua_rid; + + top_nua_rid = search_top_nua_rid(ldap_state); + + next_nua_rid = check_nua_rid_is_avail(ldap_state, + top_nua_rid); + + return next_nua_rid; +} + +/********************************************************************** +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; + + pstrcpy(filter, lp_ldap_filter()); + all_string_sub(filter, "%u", "*", sizeof(pstring)); + + rc = ldapsam_search(ldap_state, lp_ldap_suffix(), + LDAP_SCOPE_SUBTREE, filter, attr, 0, + &ldap_state->result); + + if (rc != LDAP_SUCCESS) { + DEBUG(0, ("LDAP search failed: %s\n", ldap_err2string(rc))); + DEBUG(3, ("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->ldap_struct, + ldap_state->result))); + + ldap_state->entry = ldap_first_entry(ldap_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; + + /* The rebind proc needs this *HACK*. We are not multithreaded, so + this will work, but it's not nice. */ + static_ldap_state = ldap_state; + + 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->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; + LDAPMessage *entry; + int count; + + if (ldapsam_search_one_user_by_name(ldap_state, sname, &result) != LDAP_SUCCESS) { + return NT_STATUS_NO_SUCH_USER; + } + + count = ldap_count_entries(ldap_state->ldap_struct, result); + + if (count < 1) { + DEBUG(4, + ("We don't find this user [%s] count=%d\n", sname, + count)); + return NT_STATUS_NO_SUCH_USER; + } else if (count > 1) { + DEBUG(1, + ("Duplicate entries for this user [%s] Failing. count=%d\n", sname, + count)); + return NT_STATUS_NO_SUCH_USER; + } + + entry = ldap_first_entry(ldap_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; + } + ldap_msgfree(result); + ret = NT_STATUS_OK; + } else { + ldap_msgfree(result); + } + return ret; +} + +/********************************************************************** +Get SAM_ACCOUNT entry from LDAP by rid +*********************************************************************/ +static NTSTATUS ldapsam_getsampwrid(struct pdb_methods *my_methods, SAM_ACCOUNT *user, uint32 rid) +{ + NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; + struct ldapsam_privates *ldap_state = + (struct ldapsam_privates *)my_methods->private_data; + LDAPMessage *result; + LDAPMessage *entry; + int count; + + if (ldapsam_search_one_user_by_rid(ldap_state, rid, &result) != LDAP_SUCCESS) { + return NT_STATUS_NO_SUCH_USER; + } + + count = ldap_count_entries(ldap_state->ldap_struct, result); + + if (count < 1) { + DEBUG(4, + ("We don't find this rid [%i] count=%d\n", rid, + count)); + return NT_STATUS_NO_SUCH_USER; + } else if (count > 1) { + DEBUG(1, + ("More than one user with rid [%i]. Failing. count=%d\n", rid, + count)); + return NT_STATUS_NO_SUCH_USER; + } + + entry = ldap_first_entry(ldap_state->ldap_struct, result); + if (entry) { + 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; + } + ldap_msgfree(result); + ret = NT_STATUS_OK; + } else { + ldap_msgfree(result); + } + return ret; +} + +static NTSTATUS ldapsam_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 ldapsam_getsampwrid(my_methods, user, rid); +} + +/******************************************************************** +Do the actual modification - also change a plaittext 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 pdb_add) +{ + 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,("mods is empty: nothing to modify\n")); + /* may be password change below however */ + } else { + switch(ldap_op) + { + case LDAP_MOD_ADD: + make_a_mod(&mods, LDAP_MOD_ADD, "objectclass", "account"); + rc = ldapsam_add(ldap_state, dn, mods); + break; + case LDAP_MOD_REPLACE: + rc = ldapsam_modify(ldap_state, dn ,mods); + break; + default: + DEBUG(0,("Wrong LDAP operation type: %d!\n", ldap_op)); + return NT_STATUS_UNSUCCESSFUL; + } + + if (rc!=LDAP_SUCCESS) { + char *ld_error; + ldap_get_option(ldap_state->ldap_struct, LDAP_OPT_ERROR_STRING, + &ld_error); + DEBUG(1, + ("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)); + free(ld_error); + return NT_STATUS_UNSUCCESSFUL; + } + } + +#ifdef LDAP_EXOP_X_MODIFY_PASSWD + if (!(pdb_get_acct_ctrl(newpwd)&(ACB_WSTRUST|ACB_SVRTRUST|ACB_DOMTRUST))&& + (lp_ldap_passwd_sync()!=LDAP_PASSWD_SYNC_OFF)&& + need_ldap_mod(pdb_add, newpwd, PDB_PLAINTEXT_PW)&& + (pdb_get_plaintext_passwd(newpwd)!=NULL)) { + BerElement *ber; + struct berval *bv; + char *retoid; + struct berval *retdata; + + if ((ber = ber_alloc_t(LBER_USE_DER))==NULL) { + DEBUG(0,("ber_alloc_t returns NULL\n")); + return NT_STATUS_UNSUCCESSFUL; + } + ber_printf (ber, "{"); + ber_printf (ber, "ts", LDAP_TAG_EXOP_X_MODIFY_PASSWD_ID,dn); + ber_printf (ber, "ts", LDAP_TAG_EXOP_X_MODIFY_PASSWD_NEW, pdb_get_plaintext_passwd(newpwd)); + ber_printf (ber, "N}"); + + if ((rc = ber_flatten (ber, &bv))<0) { + DEBUG(0,("ber_flatten returns a value <0\n")); + return NT_STATUS_UNSUCCESSFUL; + } + + ber_free(ber,1); + + if ((rc = ldapsam_extended_operation(ldap_state, LDAP_EXOP_X_MODIFY_PASSWD, + bv, NULL, NULL, &retoid, &retdata))!=LDAP_SUCCESS) { + DEBUG(0,("LDAP Password could not be changed for user %s: %s\n", + pdb_get_username(newpwd),ldap_err2string(rc))); + } else { + DEBUG(3,("LDAP Password changed for user %s\n",pdb_get_username(newpwd))); + + ber_bvfree(retdata); + ber_memfree(retoid); + } + ber_bvfree(bv); + } +#else + DEBUG(10,("LDAP PASSWORD SYNC is not supported!\n")); +#endif /* LDAP_EXOP_X_MODIFY_PASSWD */ + 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; + char *dn; + LDAPMessage *entry; + LDAPMessage *result; + + if (!sam_acct) { + DEBUG(0, ("sam_acct was NULL!\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + sname = pdb_get_username(sam_acct); + + DEBUG (3, ("Deleting user %s from LDAP.\n", sname)); + + rc = ldapsam_search_one_user_by_name(ldap_state, sname, &result); + if (rc != LDAP_SUCCESS) { + return NT_STATUS_NO_SUCH_USER; + } + + if (ldap_count_entries (ldap_state->ldap_struct, result) == 0) { + DEBUG (0, ("User doesn't exit!\n")); + ldap_msgfree (result); + return NT_STATUS_NO_SUCH_USER; + } + + entry = ldap_first_entry (ldap_state->ldap_struct, result); + dn = ldap_get_dn (ldap_state->ldap_struct, entry); + ldap_msgfree(result); + + rc = ldapsam_delete(ldap_state, dn); + + ldap_memfree (dn); + if (rc != LDAP_SUCCESS) { + char *ld_error; + ldap_get_option (ldap_state->ldap_struct, LDAP_OPT_ERROR_STRING, &ld_error); + DEBUG (0,("failed to delete user with uid = %s with: %s\n\t%s\n", + sname, ldap_err2string (rc), ld_error)); + free (ld_error); + return NT_STATUS_CANNOT_DELETE; + } + + DEBUG (2,("successfully deleted uid = %s from the LDAP database\n", sname)); + return NT_STATUS_OK; +} + +/********************************************************************** +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; + char *dn; + LDAPMessage *result; + LDAPMessage *entry; + LDAPMod **mods; + + if (!init_ldap_from_sam(ldap_state, &mods, LDAP_MOD_REPLACE, False, newpwd)) { + DEBUG(0, ("ldapsam_update_sam_account: init_ldap_from_sam failed!\n")); + return NT_STATUS_UNSUCCESSFUL; + } + + if (mods == NULL) { + DEBUG(4,("mods is empty: nothing to update for user: %s\n",pdb_get_username(newpwd))); + return NT_STATUS_OK; + } + + rc = ldapsam_search_one_user_by_name(ldap_state, pdb_get_username(newpwd), &result); + if (rc != LDAP_SUCCESS) { + return NT_STATUS_UNSUCCESSFUL; + } + + if (ldap_count_entries(ldap_state->ldap_struct, result) == 0) { + DEBUG(0, ("No user to modify!\n")); + ldap_msgfree(result); + return NT_STATUS_UNSUCCESSFUL; + } + + entry = ldap_first_entry(ldap_state->ldap_struct, result); + dn = ldap_get_dn(ldap_state->ldap_struct, entry); + ldap_msgfree(result); + + ret = ldapsam_modify_entry(my_methods,newpwd,dn,mods,LDAP_MOD_REPLACE, False); + if (NT_STATUS_IS_ERR(ret)) { + DEBUG(0,("failed to modify user with uid = %s\n", + pdb_get_username(newpwd))); + ldap_mods_free(mods,1); + return ret; + } + + + DEBUG(2, + ("successfully modified uid = %s in the LDAP database\n", + pdb_get_username(newpwd))); + ldap_mods_free(mods, 1); + return NT_STATUS_OK; +} + +/********************************************************************** +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; + pstring filter; + LDAPMessage *result = NULL; + pstring dn; + LDAPMod **mods = NULL; + int ldap_op; + uint32 num_result; + + const char *username = pdb_get_username(newpwd); + if (!username || !*username) { + DEBUG(0, ("Cannot add user without a username!\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + rc = ldapsam_search_one_user_by_name (ldap_state, username, &result); + if (rc != LDAP_SUCCESS) { + return NT_STATUS_UNSUCCESSFUL; + } + + if (ldap_count_entries(ldap_state->ldap_struct, result) != 0) { + DEBUG(0,("User '%s' already in the base, with samba properties\n", + username)); + ldap_msgfree(result); + return NT_STATUS_UNSUCCESSFUL; + } + ldap_msgfree(result); + + slprintf (filter, sizeof (filter) - 1, "uid=%s", username); + rc = ldapsam_search_one_user(ldap_state, filter, &result); + 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 one user with that uid exists: bailing out!\n")); + ldap_msgfree(result); + return NT_STATUS_UNSUCCESSFUL; + } + + /* Check if we need to update an existing entry */ + if (num_result == 1) { + char *tmp; + LDAPMessage *entry; + + DEBUG(3,("User exists without samba properties: adding them\n")); + ldap_op = LDAP_MOD_REPLACE; + entry = ldap_first_entry (ldap_state->ldap_struct, result); + tmp = ldap_get_dn (ldap_state->ldap_struct, entry); + slprintf (dn, sizeof (dn) - 1, "%s", tmp); + ldap_memfree (tmp); + } else { + /* Check if we need to add an entry */ + DEBUG(3,("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 ()); + } + } + + ldap_msgfree(result); + + if (!init_ldap_from_sam(ldap_state, &mods, ldap_op, True, newpwd)) { + DEBUG(0, ("ldapsam_add_sam_account: init_ldap_from_sam failed!\n")); + ldap_mods_free(mods, 1); + return NT_STATUS_UNSUCCESSFUL; + } + + if (mods == NULL) { + DEBUG(0,("mods is empty: nothing to add for user: %s\n",pdb_get_username(newpwd))); + return NT_STATUS_UNSUCCESSFUL; + } + + make_a_mod(&mods, LDAP_MOD_ADD, "objectclass", "sambaAccount"); + + ret = ldapsam_modify_entry(my_methods,newpwd,dn,mods,ldap_op, True); + if (NT_STATUS_IS_ERR(ret)) { + DEBUG(0,("failed to modify/add user with uid = %s (dn = %s)\n", + pdb_get_username(newpwd),dn)); + ldap_mods_free(mods,1); + return ret; + } + + DEBUG(2,("added: uid = %s in the LDAP database\n", pdb_get_username(newpwd))); + ldap_mods_free(mods, 1); + return NT_STATUS_OK; +} + +static void free_private_data(void **vp) +{ + struct ldapsam_privates **ldap_state = (struct ldapsam_privates **)vp; + + ldapsam_close(*ldap_state); + + if ((*ldap_state)->bind_secret) { + memset((*ldap_state)->bind_secret, '\0', strlen((*ldap_state)->bind_secret)); + } + + ldapsam_close(*ldap_state); + + SAFE_FREE((*ldap_state)->bind_dn); + SAFE_FREE((*ldap_state)->bind_secret); + + *ldap_state = NULL; + + /* No need to free any further, as it is talloc()ed */ +} + +NTSTATUS pdb_init_ldapsam(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; + + /* TODO: Setup private data and free */ + + ldap_state = talloc_zero(pdb_context->mem_ctx, sizeof(struct ldapsam_privates)); + + if (!ldap_state) { + DEBUG(0, ("talloc() failed for ldapsam private_data!\n")); + return NT_STATUS_NO_MEMORY; + } + + if (location) { + ldap_state->uri = talloc_strdup(pdb_context->mem_ctx, location); +#ifdef WITH_LDAP_SAMCONFIG + } else { + 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; + } + + ldap_state->uri = talloc_asprintf(pdb_context->mem_ctx, "%s://%s:%d", lp_ldap_ssl() == LDAP_SSL_ON ? "ldaps" : "ldap", lp_ldap_server(), ldap_port); + if (!ldap_state->uri) { + return NT_STATUS_NO_MEMORY; + } +#else + } else { + ldap_state->uri = "ldap://localhost"; +#endif + } + + (*pdb_method)->private_data = ldap_state; + + (*pdb_method)->free_private_data = free_private_data; + + return NT_STATUS_OK; +} + +NTSTATUS pdb_init_ldapsam_nua(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location) +{ + NTSTATUS nt_status; + struct ldapsam_privates *ldap_state; + uint32 low_nua_uid, high_nua_uid; + + if (!NT_STATUS_IS_OK(nt_status = pdb_init_ldapsam(pdb_context, pdb_method, location))) { + return nt_status; + } + + (*pdb_method)->name = "ldapsam_nua"; + + ldap_state = (*pdb_method)->private_data; + + ldap_state->permit_non_unix_accounts = True; + + if (!lp_non_unix_account_range(&low_nua_uid, &high_nua_uid)) { + DEBUG(0, ("cannot use ldapsam_nua without 'non unix account range' in smb.conf!\n")); + return NT_STATUS_UNSUCCESSFUL; + } + + ldap_state->low_nua_rid=fallback_pdb_uid_to_user_rid(low_nua_uid); + + ldap_state->high_nua_rid=fallback_pdb_uid_to_user_rid(high_nua_uid); + + return NT_STATUS_OK; +} + + +#else + +NTSTATUS pdb_init_ldapsam(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location) +{ + DEBUG(0, ("ldap not detected at configure time, ldapsam not availalble!\n")); + return NT_STATUS_UNSUCCESSFUL; +} + +NTSTATUS pdb_init_ldapsam_nua(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location) +{ + DEBUG(0, ("ldap not dectected at configure time, ldapsam_nua not available!\n")); + return NT_STATUS_UNSUCCESSFUL; +} + + +#endif diff --git a/source4/passdb/pdb_nisplus.c b/source4/passdb/pdb_nisplus.c new file mode 100644 index 0000000000..0a42c36ea0 --- /dev/null +++ b/source4/passdb/pdb_nisplus.c @@ -0,0 +1,1565 @@ + +/* + * NIS+ Passdb Backend + * Copyright (C) Andrew Tridgell 1992-1998 Modified by Jeremy Allison 1995. + * Copyright (C) Benny Holmgren 1998 + * Copyright (C) Luke Kenneth Casson Leighton 1996-1998. + * Copyright (C) Toomas Soome 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" + +#ifdef WITH_NISPLUS_SAM + +#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 + +#include + +/*************************************************************** + + the fields for the NIS+ table, generated from mknissmbpwtbl.sh, are: + + 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 + +****************************************************************/ + +#define NPF_NAME 0 +#define NPF_UID 1 +#define NPF_USER_RID 2 +#define NPF_SMB_GRPID 3 +#define NPF_GROUP_RID 4 +#define NPF_ACB 5 +#define NPF_LMPWD 6 +#define NPF_NTPWD 7 +#define NPF_LOGON_T 8 +#define NPF_LOGOFF_T 9 +#define NPF_KICK_T 10 +#define NPF_PWDLSET_T 11 +#define NPF_PWDCCHG_T 12 +#define NPF_PWDMCHG_T 13 +#define NPF_FULL_NAME 14 +#define NPF_HOME_DIR 15 +#define NPF_DIR_DRIVE 16 +#define NPF_LOGON_SCRIPT 17 +#define NPF_PROFILE_PATH 18 +#define NPF_ACCT_DESC 19 +#define NPF_WORKSTATIONS 20 +#define NPF_HOURS 21 + +struct nisplus_private_info { + nis_result *result; + int enum_entry; + char *location; +}; + +static char *make_nisname_from_user_rid (uint32 rid, char *pfile); +static char *make_nisname_from_name (const char *user_name, char *pfile); +static void get_single_attribute (const nis_object * new_obj, int col, + char *val, int len);; +static BOOL make_sam_from_nisp_object (SAM_ACCOUNT * pw_buf, + const nis_object * obj); +static BOOL make_sam_from_nisresult (SAM_ACCOUNT * pw_buf, + const nis_result * result);; +static void set_single_attribute (nis_object * new_obj, int col, + const char *val, int len, int flags); +static BOOL init_nisp_from_sam (nis_object * obj, const SAM_ACCOUNT * sampass, + nis_object * old); +static nis_result *nisp_get_nis_list (const char *nisname, + unsigned int flags); + +/*************************************************************** + Start enumeration of the passwd list. +****************************************************************/ + +static NTSTATUS nisplussam_setsampwent (struct pdb_methods *methods, BOOL update) +{ + struct nisplus_private_info *private = + (struct nisplus_private_info *) methods->private_data; + + char *sp; + pstring pfiletmp; + + if ((sp = strrchr (private->location, '/'))) + safe_strcpy (pfiletmp, sp + 1, sizeof (pfiletmp) - 1); + else + safe_strcpy (pfiletmp, p, sizeof (pfiletmp) - 1); + safe_strcat (pfiletmp, ".org_dir", + sizeof (pfiletmp) - strlen (pfiletmp) - 1); + + pdb_endsampwent (); /* just in case */ + global_nisp_ent->result = nisp_get_nis_list (pfiletmp, 0); + global_nisp_ent->enum_entry = 0; + if (global_nisp_ent->result != NULL) + return NT_STATUS_UNSUCCESSFUL; + else + return NT_STATUS_OK; +} + +/*************************************************************** + End enumeration of the passwd list. +****************************************************************/ + +static void nisplussam_endsampwent (struct pdb_methods *methods) +{ + struct nisplus_private_info *global_nisp_ent = + (struct nisplus_private_info *) methods->private_data; + if (global_nisp_ent->result) + nis_freeresult (global_nisp_ent->result); + global_nisp_ent->result = NULL; + global_nisp_ent->enum_entry = 0; +} + +/***************************************************************** + Get one SAM_ACCOUNT from the list (next in line) +*****************************************************************/ + +static NTSTATUS nisplussam_getsampwent (struct pdb_methods *methods, + SAM_ACCOUNT * user) +{ + NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL; + struct nisplus_private_info *global_nisp_ent = + (struct nisplus_private_info *) methods->private_data; + int enum_entry = (int) (global_nisp_ent->enum_entry); + nis_result *result = global_nisp_ent->result; + + if (user == NULL) { + DEBUG (0, ("SAM_ACCOUNT is NULL.\n")); + return nt_status; + } + + if (result == NULL || enum_entry < 0 || enum_entry >= (NIS_RES_NUMOBJ (result) - 1)) { + return nt_status; + } + + if (!make_sam_from_nisp_object(user, &NIS_RES_OBJECT (result)[enum_entry])) { + DEBUG (0, ("Bad SAM_ACCOUNT entry returned from NIS+!\n")); + return nt_status; + } + (int) (global_nisp_ent->enum_entry)++; + + return nt_status; +} + +/****************************************************************** + Lookup a name in the SAM database +******************************************************************/ + +static NTSTATUS nisplussam_getsampwnam (struct pdb_methods *methods, + SAM_ACCOUNT * user, const char *sname) +{ + /* Static buffers we will return. */ + NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL; + nis_result *result = NULL; + pstring nisname; + BOOL ret; + struct nisplus_private_info *private = + (struct nisplus_private_info *) methods->private_data; + + if (!private->location || !(*private->location)) { + DEBUG (0, ("No SMB password file set\n")); + return nt_status; + } + if (strrchr (private->location, '/')) + private->location = strrchr (private->location, '/') + 1; + + slprintf (nisname, sizeof (nisname) - 1, "[name=%s],%s.org_dir", + sname, private->location); + DEBUG (10, ("search by nisname: %s\n", nisname)); + + /* Search the table. */ + + if (!(result = nisp_get_nis_list (nisname, 0))) { + return nt_status; + } + + ret = make_sam_from_nisresult (user, result); + nis_freeresult (result); + + if (ret) nt_status = NT_STATUS_OK; + + return nt_status; +} + +/*************************************************************************** + Search by sid + **************************************************************************/ + +static NTSTATUS nisplussam_getsampwrid (struct pdb_methods *methods, + SAM_ACCOUNT * user, uint32 rid) +{ + NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL; + nis_result *result; + char *nisname; + BOOL ret; + char *sp; + pstring pfiletmp; + struct nisplus_private_info *private = + (struct nisplus_private_info *) methods->private_data; + + if (!private->location || !(*private->location)) { + DEBUG (0, ("no SMB password file set\n")); + return nt_status; + } + + if ((sp = strrchr (private->location, '/'))) + safe_strcpy (pfiletmp, sp + 1, sizeof (pfiletmp) - 1); + else + safe_strcpy (pfiletmp, private->location, sizeof (pfiletmp) - 1); + safe_strcat (pfiletmp, ".org_dir", + sizeof (pfiletmp) - strlen (pfiletmp) - 1); + + nisname = make_nisname_from_user_rid (rid, pfiletmp); + + DEBUG (10, ("search by rid: %s\n", nisname)); + + /* Search the table. */ + + if (!(result = nisp_get_nis_list (nisname, 0))) { + return nt_status; + } + + ret = make_sam_from_nisresult (user, result); + nis_freeresult (result); + + if (ret) nt_status = NT_STATUS_OK; + + return nt_status; +} + +static NTSTATUS nisplussam_getsampwsid (struct pdb_methods *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 nisplussam_getsampwrid (methods, user, rid); +} + + + +/*************************************************************************** + Delete a SAM_ACCOUNT +****************************************************************************/ + +static NTSTATUS nisplussam_delete_sam_account (struct pdb_methods *methods, + SAM_ACCOUNT * user) +{ + NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL; + const char *sname; + pstring nisname; + nis_result *result, *delresult; + nis_object *obj; + struct nisplus_private_info *private = + (struct nisplus_private_info *) methods->private_data; + + if (!user) { + DEBUG (0, ("no SAM_ACCOUNT specified!\n")); + return nt_status; + } + + sname = pdb_get_username (user); + + if (!private->location || !(*private->location)) { + DEBUG (0, ("no SMB password file set\n")); + return nt_status; + } + + if (strrchr (private->location, '/')) + private->location = strrchr (private->location, '/') + 1; + + slprintf (nisname, sizeof (nisname) - 1, "[name=%s],%s.org_dir", + sname, private->location); + + /* Search the table. */ + + if (!(result = nisp_get_nis_list (nisname, + MASTER_ONLY | FOLLOW_LINKS | + FOLLOW_PATH | EXPAND_NAME | + HARD_LOOKUP))) { + return nt_status; + } + + if (result->status != NIS_SUCCESS || NIS_RES_NUMOBJ (result) <= 0) { + /* User not found. */ + DEBUG (0, ("user not found in NIS+\n")); + nis_freeresult (result); + return nt_status; + } + + obj = NIS_RES_OBJECT (result); + slprintf (nisname, sizeof (nisname) - 1, "[name=%s],%s.%s", sname, + obj->zo_name, obj->zo_domain); + + DEBUG (10, ("removing name: %s\n", nisname)); + delresult = nis_remove_entry (nisname, obj, + MASTER_ONLY | REM_MULTIPLE | ALL_RESULTS + | FOLLOW_PATH | EXPAND_NAME | + HARD_LOOKUP); + + nis_freeresult (result); + + if (delresult->status != NIS_SUCCESS) { + DEBUG (0, ("NIS+ table update failed: %s %s\n", + nisname, nis_sperrno (delresult->status))); + nis_freeresult (delresult); + return nt_status; + } + nis_freeresult (delresult); + + return NT_STATUS_OK; +} + +/*************************************************************************** + Modifies an existing SAM_ACCOUNT +****************************************************************************/ + +static NTSTATUS nisplussam_update_sam_account (struct pdb_methods *methods, + SAM_ACCOUNT * newpwd) +{ + NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL; + nis_result *result, *addresult; + nis_object *obj; + nis_object new_obj; + entry_col *ecol; + int ta_maxcol; + struct nisplus_private_info *private = + (struct nisplus_private_info *) methods->private_data; + pstring nisname; + + if (!private->location || !(*private->location)) { + DEBUG (0, ("no SMB password file set\n")); + return nt_status; + } + if (strrchr (private->location, '/')) + private->location = strrchr (private->location, '/') + 1; + + slprintf (nisname, sizeof (nisname) - 1, "[name=%s],%s.org_dir", + pdb_get_username (newpwd), private->location); + + DEBUG (10, ("search by name: %s\n", nisname)); + + /* Search the table. */ + + if (! + (result = + nisp_get_nis_list (nisname, + MASTER_ONLY | FOLLOW_LINKS | FOLLOW_PATH | + EXPAND_NAME | HARD_LOOKUP))) { + return ne_status; + } + + if (result->status != NIS_SUCCESS || NIS_RES_NUMOBJ (result) <= 0) { + /* User not found. */ + DEBUG (0, ("user not found in NIS+\n")); + nis_freeresult (result); + return nt_status; + } + + obj = NIS_RES_OBJECT (result); + DEBUG (6, ("entry found in %s\n", obj->zo_domain)); + + /* we must create new stub object with EN_MODIFIED flag. + this is because obj from result is going to be freed and + we do not want to break it or cause memory leaks or corruption. + */ + + memmove ((char *) &new_obj, obj, sizeof (new_obj)); + ta_maxcol = obj->TA_data.ta_maxcol; + + if (!(ecol = (entry_col *) malloc (ta_maxcol * sizeof (entry_col)))) { + DEBUG (0, ("memory allocation failure\n")); + nis_freeresult (result); + return nt_status; + } + + memmove ((char *) ecol, obj->EN_data.en_cols.en_cols_val, + ta_maxcol * sizeof (entry_col)); + new_obj.EN_data.en_cols.en_cols_val = ecol; + new_obj.EN_data.en_cols.en_cols_len = ta_maxcol; + + if (init_nisp_from_sam (&new_obj, newpwd, obj) == True) { + slprintf (nisname, sizeof (nisname) - 1, "[name=%s],%s.%s", + pdb_get_username (newpwd), private->location, obj->zo_domain); + + DEBUG (10, ("NIS+ table update: %s\n", nisname)); + addresult = + nis_modify_entry (nisname, &new_obj, + MOD_SAMEOBJ | FOLLOW_PATH | + EXPAND_NAME | HARD_LOOKUP); + + if (addresult->status != NIS_SUCCESS) { + DEBUG (0, ("NIS+ table update failed: %s %s\n", + nisname, nis_sperrno (addresult->status))); + nis_freeresult (addresult); + nis_freeresult (result); + free (ecol); + return nt_status; + } + + DEBUG (6, ("password changed\n")); + nis_freeresult (addresult); + } else { + DEBUG (6, ("nothing to change!\n")); + } + + free (ecol); + nis_freeresult (result); + + return NT_STATUS_OK; +} + +/*************************************************************************** + Adds an existing SAM_ACCOUNT +****************************************************************************/ + +static NTSTATUS nisplussam_add_sam_account (struct pdb_methods *methods, + SAM_ACCOUNT * newpwd) +{ + NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL; + int local_user = 0; + char *pfile; + pstring pfiletmp; + char *nisname; + nis_result *result = NULL, *tblresult = NULL; + nis_object new_obj; + entry_col *ecol; + int ta_maxcol; + + /* + * 1. find user domain. + * a. try nis search in passwd.org_dir - if found use domain from result. + * b. try getpwnam. this may be needed if user is defined + * in /etc/passwd file (or elsewere) and not in passwd.org_dir. + * if found, use host default domain. + * c. exit with False - no such user. + * + * 2. add user + * a. find smbpasswd table + * search pfile in user domain if not found, try host default + * domain. + * b. smbpasswd domain is found, fill data and add entry. + * + * pfile should contain ONLY table name, org_dir will be concated. + * so, at first we will clear path prefix from pfile, and + * then we will use pfiletmp as playground to put together full + * nisname string. + * such approach will make it possible to specify samba private dir + * AND still use NIS+ table. as all domain related data is normally + * stored in org_dir.DOMAIN, this should be ok do do. + */ + + pfile = private->location; + if (strrchr (pfile, '/')) + pfile = strrchr (pfile, '/') + 1; + + /* + * Check if user is already there. + */ + safe_strcpy (pfiletmp, pfile, sizeof (pfiletmp) - 1); + safe_strcat (pfiletmp, ".org_dir", + sizeof (pfiletmp) - strlen (pfiletmp) - 1); + + if (pdb_get_username (newpwd) != NULL) { + nisname = make_nisname_from_name (pdb_get_username (newpwd), + pfiletmp); + } else { + return nt_status; + } + + if (! + (result = + nisp_get_nis_list (nisname, + MASTER_ONLY | FOLLOW_LINKS | FOLLOW_PATH | + EXPAND_NAME | HARD_LOOKUP))) { + return nt_status; + } + if (result->status != NIS_SUCCESS && result->status != NIS_NOTFOUND) { + DEBUG (3, ("nis_list failure: %s: %s\n", + nisname, nis_sperrno (result->status))); + nis_freeresult (result); + return nt_status; + } + + if (result->status == NIS_SUCCESS && NIS_RES_NUMOBJ (result) > 0) { + DEBUG (3, ("User already exists in NIS+ password db: %s\n", + pfile)); + nis_freeresult (result); + return nt_status; + } + + nis_freeresult (result); /* no such user, free results */ + + /* + * check for user in unix password database. we need this to get + * domain, where smbpasswd entry should be stored. + */ + + nisname = make_nisname_from_name (pdb_get_username (newpwd), + "passwd.org_dir"); + + result = nisp_get_nis_list (nisname, + MASTER_ONLY | FOLLOW_LINKS | FOLLOW_PATH | + EXPAND_NAME | HARD_LOOKUP); + + if (result->status != NIS_SUCCESS || NIS_RES_NUMOBJ (result) <= 0) { + struct passwd *passwd; + + DEBUG (3, ("nis_list failure: %s: %s\n", + nisname, nis_sperrno (result->status))); + nis_freeresult (result); + + if (!(passwd = getpwnam_alloc (pdb_get_username (newpwd)))) { + /* no such user in system! */ + return nt_status; + } + passwd_free (&passwd); + + /* + * user is defined, but not in passwd.org_dir. + */ + local_user = 1; + } else { + safe_strcpy (pfiletmp, pfile, sizeof (pfiletmp) - 1); + safe_strcat (pfiletmp, ".", + sizeof (pfiletmp) - strlen (pfiletmp) - 1); + safe_strcat (pfiletmp, NIS_RES_OBJECT (result)->zo_domain, + sizeof (pfiletmp) - strlen (pfiletmp) - 1); + nis_freeresult (result); /* not needed any more */ + + tblresult = nisp_get_nis_list (pfiletmp, + MASTER_ONLY | FOLLOW_LINKS | + FOLLOW_PATH | EXPAND_NAME | + HARD_LOOKUP); + } + + if (local_user || tblresult->status != NIS_SUCCESS) { + /* + * no user domain or + * smbpasswd table not found in user domain, fallback to + * default domain. + */ + if (!local_user) /* free previous failed search result */ + nis_freeresult (tblresult); + + safe_strcpy (pfiletmp, pfile, sizeof (pfiletmp) - 1); + safe_strcat (pfiletmp, ".org_dir", + sizeof (pfiletmp) - strlen (pfiletmp) - 1); + tblresult = nis_lookup (pfiletmp, MASTER_ONLY | FOLLOW_LINKS | + FOLLOW_PATH | EXPAND_NAME | + HARD_LOOKUP); + if (tblresult->status != NIS_SUCCESS) { + /* still nothing. bail out */ + nis_freeresult (tblresult); + DEBUG (3, ("nis_lookup failure: %s\n", + nis_sperrno (tblresult->status))); + return nt_status; + } + /* we need full name for nis_add_entry() */ + safe_strcpy (pfiletmp, pfile, sizeof (pfiletmp) - 1); + safe_strcat (pfiletmp, ".", + sizeof (pfiletmp) - strlen (pfiletmp) - 1); + safe_strcat (pfiletmp, NIS_RES_OBJECT (tblresult)->zo_domain, + sizeof (pfiletmp) - strlen (pfiletmp) - 1); + } + + memset ((char *) &new_obj, 0, sizeof (new_obj)); + /* fill entry headers */ + /* we do not free these. */ + new_obj.zo_name = NIS_RES_OBJECT (tblresult)->zo_name; + new_obj.zo_owner = NIS_RES_OBJECT (tblresult)->zo_owner; + new_obj.zo_group = NIS_RES_OBJECT (tblresult)->zo_group; + new_obj.zo_domain = NIS_RES_OBJECT (tblresult)->zo_domain; + /* uints */ + new_obj.zo_access = NIS_RES_OBJECT (tblresult)->zo_access; + new_obj.zo_ttl = NIS_RES_OBJECT (tblresult)->zo_ttl; + + new_obj.zo_data.zo_type = ENTRY_OBJ; + new_obj.EN_data.en_type = NIS_RES_OBJECT (tblresult)->TA_data.ta_type; + + ta_maxcol = NIS_RES_OBJECT (tblresult)->TA_data.ta_maxcol; + + if (!(ecol = (entry_col *) malloc (ta_maxcol * sizeof (entry_col)))) { + DEBUG (0, ("memory allocation failure\n")); + nis_freeresult (tblresult); + return nt_status; + } + + memset ((char *) ecol, 0, ta_maxcol * sizeof (entry_col)); + new_obj.EN_data.en_cols.en_cols_val = ecol; + new_obj.EN_data.en_cols.en_cols_len = ta_maxcol; + + init_nisp_from_sam (&new_obj, newpwd, NULL); + + DEBUG (10, ("add NIS+ entry: %s\n", nisname)); + result = nis_add_entry (pfiletmp, &new_obj, 0); + + free (ecol); /* free allocated entry space */ + + if (result->status != NIS_SUCCESS) { + DEBUG (3, ("NIS+ table update failed: %s,%s\n", + nisname, nis_sperrno (result->status))); + nis_freeresult (tblresult); + nis_freeresult (result); + return nt_status; + } + + nis_freeresult (tblresult); + nis_freeresult (result); + + return NT_STATUS_OK; +} + +/*************************************************************** + make_nisname_from_user_rid + ****************************************************************/ +static char *make_nisname_from_user_rid (uint32 rid, char *pfile) +{ + static pstring nisname; + + safe_strcpy (nisname, "[user_rid=", sizeof (nisname) - 1); + slprintf (nisname, sizeof (nisname) - 1, "%s%d", nisname, rid); + safe_strcat (nisname, "],", sizeof (nisname) - strlen (nisname) - 1); + safe_strcat (nisname, pfile, sizeof (nisname) - strlen (nisname) - 1); + + return nisname; +} + +/*************************************************************** + make_nisname_from_name + ****************************************************************/ +static char *make_nisname_from_name (const char *user_name, char *pfile) +{ + static pstring nisname; + + safe_strcpy (nisname, "[name=", sizeof (nisname) - 1); + safe_strcat (nisname, user_name, + sizeof (nisname) - strlen (nisname) - 1); + safe_strcat (nisname, "],", sizeof (nisname) - strlen (nisname) - 1); + safe_strcat (nisname, pfile, sizeof (nisname) - strlen (nisname) - 1); + + return nisname; +} + +/************************************************************************* + gets a NIS+ attribute + *************************************************************************/ +static void get_single_attribute (const nis_object * new_obj, int col, + char *val, int len) +{ + int entry_len; + + if (new_obj == NULL || val == NULL) + return; + + entry_len = ENTRY_LEN (new_obj, col); + if (len > entry_len) { + len = entry_len; + } + + safe_strcpy (val, ENTRY_VAL (new_obj, col), len - 1); +} + +/************************************************************************ + makes a struct sam_passwd from a NIS+ object. + ************************************************************************/ +static BOOL make_sam_from_nisp_object (SAM_ACCOUNT * pw_buf, + const nis_object * obj) +{ + char *ptr; + pstring full_name; /* this must be translated to dos code page */ + pstring acct_desc; /* this must be translated to dos code page */ + pstring home_dir; /* set default value from smb.conf for user */ + pstring home_drive; /* set default value from smb.conf for user */ + pstring logon_script; /* set default value from smb.conf for user */ + pstring profile_path; /* set default value from smb.conf for user */ + pstring hours; + int hours_len; + unsigned char smbpwd[16]; + unsigned char smbntpwd[16]; + + + /* + * time values. note: this code assumes 32bit time_t! + */ + + /* Don't change these timestamp settings without a good reason. They are + important for NT member server compatibility. */ + + pdb_set_logon_time (pw_buf, (time_t) 0, PDB_DEFAULT); + ptr = (uchar *) ENTRY_VAL (obj, NPF_LOGON_T); + if (ptr && *ptr && (StrnCaseCmp (ptr, "LNT-", 4) == 0)) { + int i; + + ptr += 4; + for (i = 0; i < 8; i++) { + if (ptr[i] == '\0' || !isxdigit (ptr[i])) + break; + } + if (i == 8) { + pdb_set_logon_time (pw_buf, + (time_t) strtol (ptr, NULL, 16), + PDB_SET); + } + } + + pdb_set_logoff_time (pw_buf, get_time_t_max (), PDB_DEFAULT); + ptr = (uchar *) ENTRY_VAL (obj, NPF_LOGOFF_T); + if (ptr && *ptr && (StrnCaseCmp (ptr, "LOT-", 4) == 0)) { + int i; + + ptr += 4; + for (i = 0; i < 8; i++) { + if (ptr[i] == '\0' || !isxdigit (ptr[i])) + break; + } + if (i == 8) { + pdb_set_logoff_time (pw_buf, + (time_t) strtol (ptr, NULL, 16), + PDB_SET); + } + } + + pdb_set_kickoff_time (pw_buf, get_time_t_max (), PDB_DEFAULT); + ptr = (uchar *) ENTRY_VAL (obj, NPF_KICK_T); + if (ptr && *ptr && (StrnCaseCmp (ptr, "KOT-", 4) == 0)) { + int i; + + ptr += 4; + for (i = 0; i < 8; i++) { + if (ptr[i] == '\0' || !isxdigit (ptr[i])) + break; + } + if (i == 8) { + pdb_set_kickoff_time (pw_buf, + (time_t) strtol (ptr, NULL, 16), + PDB_SET); + } + } + + pdb_set_pass_last_set_time (pw_buf, (time_t) 0, PDB_DEFAULT); + ptr = (uchar *) ENTRY_VAL (obj, NPF_PWDLSET_T); + if (ptr && *ptr && (StrnCaseCmp (ptr, "LCT-", 4) == 0)) { + int i; + + ptr += 4; + for (i = 0; i < 8; i++) { + if (ptr[i] == '\0' || !isxdigit (ptr[i])) + break; + } + if (i == 8) { + pdb_set_pass_last_set_time (pw_buf, + (time_t) strtol (ptr, + NULL, + 16), + PDB_SET); + } + } + + pdb_set_pass_can_change_time (pw_buf, (time_t) 0, PDB_DEFAULT); + ptr = (uchar *) ENTRY_VAL (obj, NPF_PWDCCHG_T); + if (ptr && *ptr && (StrnCaseCmp (ptr, "CCT-", 4) == 0)) { + int i; + + ptr += 4; + for (i = 0; i < 8; i++) { + if (ptr[i] == '\0' || !isxdigit (ptr[i])) + break; + } + if (i == 8) { + pdb_set_pass_can_change_time (pw_buf, + (time_t) strtol (ptr, + NULL, + 16), + PDB_SET); + } + } + + pdb_set_pass_must_change_time (pw_buf, get_time_t_max (), PDB_DEFAULT); /* Password never expires. */ + ptr = (uchar *) ENTRY_VAL (obj, NPF_PWDMCHG_T); + if (ptr && *ptr && (StrnCaseCmp (ptr, "MCT-", 4) == 0)) { + int i; + + ptr += 4; + for (i = 0; i < 8; i++) { + if (ptr[i] == '\0' || !isxdigit (ptr[i])) + break; + } + if (i == 8) { + pdb_set_pass_must_change_time (pw_buf, + (time_t) strtol (ptr, + NULL, + 16), + PDB_SET); + } + } + + /* string values */ + pdb_set_username (pw_buf, ENTRY_VAL (obj, NPF_NAME), PDB_SET); + pdb_set_domain (pw_buf, lp_workgroup (), PDB_DEFAULT); + /* pdb_set_nt_username() -- cant set it here... */ + + get_single_attribute (obj, NPF_FULL_NAME, full_name, + sizeof (pstring)); +#if 0 + unix_to_dos (full_name, True); +#endif + pdb_set_fullname (pw_buf, full_name, PDB_SET); + + pdb_set_acct_ctrl (pw_buf, pdb_decode_acct_ctrl (ENTRY_VAL (obj, + NPF_ACB), PDB_SET)); + + get_single_attribute (obj, NPF_ACCT_DESC, acct_desc, + sizeof (pstring)); +#if 0 + unix_to_dos (acct_desc, True); +#endif + pdb_set_acct_desc (pw_buf, acct_desc, PDB_SET); + + pdb_set_workstations (pw_buf, ENTRY_VAL (obj, NPF_WORKSTATIONS), PDB_SET); + pdb_set_munged_dial (pw_buf, NULL, PDB_DEFAULT); + + pdb_set_uid (pw_buf, atoi (ENTRY_VAL (obj, NPF_UID)), PDB_SET); + pdb_set_gid (pw_buf, atoi (ENTRY_VAL (obj, NPF_SMB_GRPID)), PDB_SET); + pdb_set_user_sid_from_rid (pw_buf, + atoi (ENTRY_VAL (obj, NPF_USER_RID)), PDB_SET); + pdb_set_group_sid_from_rid (pw_buf, + atoi (ENTRY_VAL (obj, NPF_GROUP_RID)), PDB_SET); + + /* values, must exist for user */ + if (!(pdb_get_acct_ctrl (pw_buf) & ACB_WSTRUST)) { + + get_single_attribute (obj, NPF_HOME_DIR, home_dir, + sizeof (pstring)); + if (!(home_dir && *home_dir)) { + pstrcpy (home_dir, lp_logon_home ()); + pdb_set_homedir (pw_buf, home_dir, PDB_DEFAULT); + } else + pdb_set_homedir (pw_buf, home_dir, PDB_SET); + + get_single_attribute (obj, NPF_DIR_DRIVE, home_drive, + sizeof (pstring)); + if (!(home_drive && *home_drive)) { + pstrcpy (home_drive, lp_logon_drive ()); + pdb_set_dir_drive (pw_buf, home_drive, PDB_DEFAULT); + } else + pdb_set_dir_drive (pw_buf, home_drive, PDB_SET); + + get_single_attribute (obj, NPF_LOGON_SCRIPT, logon_script, + sizeof (pstring)); + if (!(logon_script && *logon_script)) { + pstrcpy (logon_script, lp_logon_script ()); + pdb_set_logon_script (pw_buf, logon_script, PDB_DEFAULT); + } else + pdb_set_logon_script (pw_buf, logon_script, PDB_SET); + + get_single_attribute (obj, NPF_PROFILE_PATH, profile_path, + sizeof (pstring)); + if (!(profile_path && *profile_path)) { + pstrcpy (profile_path, lp_logon_path ()); + pdb_set_profile_path (pw_buf, profile_path, PDB_DEFAULT); + } else + pdb_set_profile_path (pw_buf, profile_path, PDB_SET); + + } else { + /* lkclXXXX this is OBSERVED behaviour by NT PDCs, enforced here. */ + pdb_set_group_sid_from_rid (pw_buf, DOMAIN_GROUP_RID_USERS, PDB_DEFAULT); + } + + /* Check the lanman password column. */ + ptr = (char *) ENTRY_VAL (obj, NPF_LMPWD); + if (!pdb_set_lanman_passwd (pw_buf, NULL, PDB_DEFAULT)) + return False; + + if (!strncasecmp (ptr, "NO PASSWORD", 11)) { + pdb_set_acct_ctrl (pw_buf, + pdb_get_acct_ctrl (pw_buf) | ACB_PWNOTREQ, PDB_SET); + } else { + if (strlen (ptr) != 32 || !pdb_gethexpwd (ptr, smbpwd)) { + DEBUG (0, ("malformed LM pwd entry: %s.\n", + pdb_get_username (pw_buf))); + return False; + } + if (!pdb_set_lanman_passwd (pw_buf, smbpwd, PDB_SET)) + return False; + } + + /* Check the NT password column. */ + ptr = ENTRY_VAL (obj, NPF_NTPWD); + if (!pdb_set_nt_passwd (pw_buf, NULL, PDB_DEFAULT)) + return False; + + if (!(pdb_get_acct_ctrl (pw_buf) & ACB_PWNOTREQ) && + strncasecmp (ptr, "NO PASSWORD", 11)) { + if (strlen (ptr) != 32 || !pdb_gethexpwd (ptr, smbntpwd)) { + DEBUG (0, ("malformed NT pwd entry:\ + uid = %d.\n", pdb_get_uid (pw_buf))); + return False; + } + if (!pdb_set_nt_passwd (pw_buf, smbntpwd, PDB_SET)) + return False; + } + + pdb_set_unknown_3 (pw_buf, 0xffffff, PDB_DEFAULT); /* don't know */ + pdb_set_logon_divs (pw_buf, 168, PDB_DEFAULT); /* hours per week */ + + if ((hours_len = ENTRY_LEN (obj, NPF_HOURS)) == 21) { + memcpy (hours, ENTRY_VAL (obj, NPF_HOURS), hours_len); + } else { + hours_len = 21; /* 21 times 8 bits = 168 */ + /* available at all hours */ + memset (hours, 0xff, hours_len); + } + pdb_set_hours_len (pw_buf, hours_len, PDB_SET); + pdb_set_hours (pw_buf, hours, PDB_SET); + + pdb_set_unknown_5 (pw_buf, 0x00020000, PDB_DEFAULT); /* don't know */ + pdb_set_unknown_6 (pw_buf, 0x000004ec, PDB_DEFAULT); /* don't know */ + + return True; +} + +/************************************************************************ + makes a struct sam_passwd from a NIS+ result. + ************************************************************************/ +static BOOL make_sam_from_nisresult (SAM_ACCOUNT * pw_buf, + const nis_result * result) +{ + if (pw_buf == NULL || result == NULL) + return False; + + if (result->status != NIS_SUCCESS && result->status != NIS_NOTFOUND) { + DEBUG (0, ("NIS+ lookup failure: %s\n", + nis_sperrno (result->status))); + return False; + } + + /* User not found. */ + if (NIS_RES_NUMOBJ (result) <= 0) { + DEBUG (10, ("user not found in NIS+\n")); + return False; + } + + if (NIS_RES_NUMOBJ (result) > 1) { + DEBUG (10, + ("WARNING: Multiple entries for user in NIS+ table!\n")); + } + + /* Grab the first hit. */ + return make_sam_from_nisp_object (pw_buf, + &NIS_RES_OBJECT (result)[0]); +} + +/************************************************************************* + sets a NIS+ attribute + *************************************************************************/ +static void set_single_attribute (nis_object * new_obj, int col, + const char *val, int len, int flags) +{ + if (new_obj == NULL) + return; + + ENTRY_VAL (new_obj, col) = val; + ENTRY_LEN (new_obj, col) = len + 1; + + if (flags != 0) { + new_obj->EN_data.en_cols.en_cols_val[col].ec_flags = flags; + } +} + +/*************************************************************** + copy or modify nis object. this object is used to add or update + nisplus table entry. + ****************************************************************/ +static BOOL init_nisp_from_sam (nis_object * obj, const SAM_ACCOUNT * sampass, + nis_object * old) +{ + /* + * Fill nis_object for entry add or update. + * if we are updateing, we have to find out differences and set + * EN_MODIFIED flag. also set need_to_modify to trigger + * nis_modify_entry() call in pdb_update_sam_account(). + * + * TODO: + * get data from SAM + * if (modify) get data from nis_object, compare and store if + * different + set EN_MODIFIED and need_to_modify + * else + * store + */ + BOOL need_to_modify = False; + const char *name = pdb_get_username (sampass); /* from SAM */ + + /* these must be static or allocate and free entry columns! */ + static fstring uid; /* from SAM */ + static fstring user_rid; /* from SAM */ + static fstring gid; /* from SAM */ + static fstring group_rid; /* from SAM */ + char *acb; /* from SAM */ + static fstring smb_passwd; /* from SAM */ + static fstring smb_nt_passwd; /* from SAM */ + static fstring logon_t; /* from SAM */ + static fstring logoff_t; /* from SAM */ + static fstring kickoff_t; /* from SAM */ + static fstring pwdlset_t; /* from SAM */ + static fstring pwdlchg_t; /* from SAM */ + static fstring pwdmchg_t; /* from SAM */ + static fstring full_name; /* from SAM */ + static fstring acct_desc; /* from SAM */ + static char empty[1]; /* just an empty string */ + + slprintf (uid, sizeof (uid) - 1, "%u", pdb_get_uid (sampass)); + slprintf (user_rid, sizeof (user_rid) - 1, "%u", + pdb_get_user_rid (sampass) ? pdb_get_user_rid (sampass) : + fallback_pdb_uid_to_user_rid (pdb_get_uid (sampass))); + slprintf (gid, sizeof (gid) - 1, "%u", pdb_get_gid (sampass)); + + { + uint32 rid; + GROUP_MAP map; + + rid = pdb_get_group_rid (sampass); + + if (rid == 0) { + if (pdb_getgrgid(&map, pdb_get_gid (sampass), + MAPPING_WITHOUT_PRIV)) { + if (!sid_peek_check_rid + (get_global_sam_sid (), &map.sid, &rid)) + return False; + } else + rid = pdb_gid_to_group_rid (pdb_get_gid + (sampass)); + } + + slprintf (group_rid, sizeof (group_rid) - 1, "%u", rid); + } + + acb = pdb_encode_acct_ctrl (pdb_get_acct_ctrl (sampass), + NEW_PW_FORMAT_SPACE_PADDED_LEN); + pdb_sethexpwd (smb_passwd, pdb_get_lanman_passwd (sampass), + pdb_get_acct_ctrl (sampass)); + pdb_sethexpwd (smb_nt_passwd, pdb_get_nt_passwd (sampass), + pdb_get_acct_ctrl (sampass)); + slprintf (logon_t, 13, "LNT-%08X", + (uint32) pdb_get_logon_time (sampass)); + slprintf (logoff_t, 13, "LOT-%08X", + (uint32) pdb_get_logoff_time (sampass)); + slprintf (kickoff_t, 13, "KOT-%08X", + (uint32) pdb_get_kickoff_time (sampass)); + slprintf (pwdlset_t, 13, "LCT-%08X", + (uint32) pdb_get_pass_last_set_time (sampass)); + slprintf (pwdlchg_t, 13, "CCT-%08X", + (uint32) pdb_get_pass_can_change_time (sampass)); + slprintf (pwdmchg_t, 13, "MCT-%08X", + (uint32) pdb_get_pass_must_change_time (sampass)); + safe_strcpy (full_name, pdb_get_fullname (sampass), + sizeof (full_name) - 1); + safe_strcpy (acct_desc, pdb_get_acct_desc (sampass), + sizeof (acct_desc) - 1); + +#if 0 + + /* Not sure what to do with these guys. -tpot */ + + dos_to_unix (full_name, True); + dos_to_unix (acct_desc, True); + +#endif + + if (old) { + /* name */ + if (strcmp (ENTRY_VAL (old, NPF_NAME), name)) { + need_to_modify = True; + set_single_attribute (obj, NPF_NAME, name, + strlen (name), EN_MODIFIED); + } + + + /* uid */ + if (pdb_get_uid (sampass) != -1) { + if (!ENTRY_VAL (old, NPF_UID) + || strcmp (ENTRY_VAL (old, NPF_UID), uid)) { + need_to_modify = True; + set_single_attribute (obj, NPF_UID, uid, + strlen (uid), + EN_MODIFIED); + } + } + + /* user_rid */ + if (pdb_get_user_rid (sampass)) { + if (!ENTRY_VAL (old, NPF_USER_RID) || + strcmp (ENTRY_VAL (old, NPF_USER_RID), + user_rid)) { + need_to_modify = True; + set_single_attribute (obj, NPF_USER_RID, + user_rid, + strlen (user_rid), + EN_MODIFIED); + } + } + + /* smb_grpid */ + if (pdb_get_gid (sampass) != -1) { + if (!ENTRY_VAL (old, NPF_SMB_GRPID) || + strcmp (ENTRY_VAL (old, NPF_SMB_GRPID), gid)) { + need_to_modify = True; + set_single_attribute (obj, NPF_SMB_GRPID, gid, + strlen (gid), + EN_MODIFIED); + } + } + + /* group_rid */ + if (pdb_get_group_rid (sampass)) { + if (!ENTRY_VAL (old, NPF_GROUP_RID) || + strcmp (ENTRY_VAL (old, NPF_GROUP_RID), + group_rid)) { + need_to_modify = True; + set_single_attribute (obj, NPF_GROUP_RID, + group_rid, + strlen (group_rid), + EN_MODIFIED); + } + } + + /* acb */ + if (!ENTRY_VAL (old, NPF_ACB) || + strcmp (ENTRY_VAL (old, NPF_ACB), acb)) { + need_to_modify = True; + set_single_attribute (obj, NPF_ACB, acb, strlen (acb), + EN_MODIFIED); + } + + /* lmpwd */ + if (!ENTRY_VAL (old, NPF_LMPWD) || + strcmp (ENTRY_VAL (old, NPF_LMPWD), smb_passwd)) { + need_to_modify = True; + set_single_attribute (obj, NPF_LMPWD, smb_passwd, + strlen (smb_passwd), + EN_CRYPT | EN_MODIFIED); + } + + /* ntpwd */ + if (!ENTRY_VAL (old, NPF_NTPWD) || + strcmp (ENTRY_VAL (old, NPF_NTPWD), smb_nt_passwd)) { + need_to_modify = True; + set_single_attribute (obj, NPF_NTPWD, smb_nt_passwd, + strlen (smb_nt_passwd), + EN_CRYPT | EN_MODIFIED); + } + + /* logon_t */ + if (pdb_get_logon_time (sampass) && + (!ENTRY_VAL (old, NPF_LOGON_T) || + strcmp (ENTRY_VAL (old, NPF_LOGON_T), logon_t))) { + need_to_modify = True; + set_single_attribute (obj, NPF_LOGON_T, logon_t, + strlen (logon_t), EN_MODIFIED); + } + + /* logoff_t */ + if (pdb_get_logoff_time (sampass) && + (!ENTRY_VAL (old, NPF_LOGOFF_T) || + strcmp (ENTRY_VAL (old, NPF_LOGOFF_T), logoff_t))) { + need_to_modify = True; + set_single_attribute (obj, NPF_LOGOFF_T, logoff_t, + strlen (logoff_t), EN_MODIFIED); + } + + /* kick_t */ + if (pdb_get_kickoff_time (sampass) && + (!ENTRY_VAL (old, NPF_KICK_T) || + strcmp (ENTRY_VAL (old, NPF_KICK_T), kickoff_t))) { + need_to_modify = True; + set_single_attribute (obj, NPF_KICK_T, kickoff_t, + strlen (kickoff_t), + EN_MODIFIED); + } + + /* pwdlset_t */ + if (pdb_get_pass_last_set_time (sampass) && + (!ENTRY_VAL (old, NPF_PWDLSET_T) || + strcmp (ENTRY_VAL (old, NPF_PWDLSET_T), pwdlset_t))) { + need_to_modify = True; + set_single_attribute (obj, NPF_PWDLSET_T, pwdlset_t, + strlen (pwdlset_t), + EN_MODIFIED); + } + + /* pwdlchg_t */ + if (pdb_get_pass_can_change_time (sampass) && + (!ENTRY_VAL (old, NPF_PWDCCHG_T) || + strcmp (ENTRY_VAL (old, NPF_PWDCCHG_T), pwdlchg_t))) { + need_to_modify = True; + set_single_attribute (obj, NPF_PWDCCHG_T, pwdlchg_t, + strlen (pwdlchg_t), + EN_MODIFIED); + } + + /* pwdmchg_t */ + if (pdb_get_pass_must_change_time (sampass) && + (!ENTRY_VAL (old, NPF_PWDMCHG_T) || + strcmp (ENTRY_VAL (old, NPF_PWDMCHG_T), pwdmchg_t))) { + need_to_modify = True; + set_single_attribute (obj, NPF_PWDMCHG_T, pwdmchg_t, + strlen (pwdmchg_t), + EN_MODIFIED); + } + + /* full_name */ + /* must support set, unset and change */ + if ((pdb_get_fullname (sampass) && + !ENTRY_VAL (old, NPF_FULL_NAME)) || + (ENTRY_VAL (old, NPF_FULL_NAME) && + !pdb_get_fullname (sampass)) || + (ENTRY_VAL (old, NPF_FULL_NAME) && + pdb_get_fullname (sampass) && + strcmp (ENTRY_VAL (old, NPF_FULL_NAME), full_name))) { + need_to_modify = True; + set_single_attribute (obj, NPF_FULL_NAME, full_name, + strlen (full_name), + EN_MODIFIED); + } + + /* home_dir */ + /* must support set, unset and change */ + if ((pdb_get_homedir (sampass) && + !ENTRY_VAL (old, NPF_HOME_DIR)) || + (ENTRY_VAL (old, NPF_HOME_DIR) && + !pdb_get_homedir (sampass)) || + (ENTRY_VAL (old, NPF_HOME_DIR) && + pdb_get_homedir (sampass) && + strcmp (ENTRY_VAL (old, NPF_HOME_DIR), + pdb_get_homedir (sampass)))) { + need_to_modify = True; + set_single_attribute (obj, NPF_HOME_DIR, + pdb_get_homedir (sampass), + strlen (pdb_get_homedir + (sampass)), + EN_MODIFIED); + } + + /* dir_drive */ + /* must support set, unset and change */ + if ((pdb_get_dir_drive (sampass) && + !ENTRY_VAL (old, NPF_DIR_DRIVE)) || + (ENTRY_VAL (old, NPF_DIR_DRIVE) && + !pdb_get_dir_drive (sampass)) || + (ENTRY_VAL (old, NPF_DIR_DRIVE) && + pdb_get_dir_drive (sampass) && + strcmp (ENTRY_VAL (old, NPF_DIR_DRIVE), + pdb_get_dir_drive (sampass)))) { + need_to_modify = True; + set_single_attribute (obj, NPF_DIR_DRIVE, + pdb_get_dir_drive (sampass), + strlen (pdb_get_dir_drive + (sampass)), + EN_MODIFIED); + } + + /* logon_script */ + /* must support set, unset and change */ + if (((pdb_get_logon_script (sampass) && + !ENTRY_VAL (old, NPF_LOGON_SCRIPT)) || + ((ENTRY_VAL (old, NPF_LOGON_SCRIPT) && + (!pdb_get_logon_script (sampass)))) || + ((ENTRY_VAL (old, NPF_LOGON_SCRIPT) && + pdb_get_logon_script (sampass) && + strcmp (ENTRY_VAL (old, NPF_LOGON_SCRIPT), + pdb_get_logon_script (sampass)))))) { + need_to_modify = True; + set_single_attribute (obj, NPF_LOGON_SCRIPT, + pdb_get_logon_script (sampass), + strlen (pdb_get_logon_script + (sampass)), + EN_MODIFIED); + } + + /* profile_path */ + /* must support set, unset and change */ + if ((pdb_get_profile_path (sampass) && + !ENTRY_VAL (old, NPF_PROFILE_PATH)) || + (ENTRY_VAL (old, NPF_PROFILE_PATH) && + !pdb_get_profile_path (sampass)) || + (ENTRY_VAL (old, NPF_PROFILE_PATH) && + pdb_get_profile_path (sampass) && + strcmp (ENTRY_VAL (old, NPF_PROFILE_PATH), + pdb_get_profile_path (sampass)))) { + need_to_modify = True; + set_single_attribute (obj, NPF_PROFILE_PATH, + pdb_get_profile_path (sampass), + strlen (pdb_get_profile_path + (sampass)), + EN_MODIFIED); + } + + /* acct_desc */ + /* must support set, unset and change */ + if ((pdb_get_acct_desc (sampass) && + !ENTRY_VAL (old, NPF_ACCT_DESC)) || + (ENTRY_VAL (old, NPF_ACCT_DESC) && + !pdb_get_acct_desc (sampass)) || + (ENTRY_VAL (old, NPF_ACCT_DESC) && + pdb_get_acct_desc (sampass) && + strcmp (ENTRY_VAL (old, NPF_ACCT_DESC), acct_desc))) { + need_to_modify = True; + set_single_attribute (obj, NPF_ACCT_DESC, acct_desc, + strlen (acct_desc), + EN_MODIFIED); + } + + /* workstations */ + /* must support set, unset and change */ + if ((pdb_get_workstations (sampass) && + !ENTRY_VAL (old, NPF_WORKSTATIONS)) || + (ENTRY_VAL (old, NPF_WORKSTATIONS) && + !pdb_get_workstations (sampass)) || + (ENTRY_VAL (old, NPF_WORKSTATIONS) && + (pdb_get_workstations (sampass)) && + strcmp (ENTRY_VAL (old, NPF_WORKSTATIONS), + pdb_get_workstations (sampass)))) { + need_to_modify = True; + set_single_attribute (obj, NPF_WORKSTATIONS, + pdb_get_workstations (sampass), + strlen (pdb_get_workstations + (sampass)), + EN_MODIFIED); + } + + /* hours */ + if ((pdb_get_hours_len (sampass) != + ENTRY_LEN (old, NPF_HOURS)) + || memcmp (pdb_get_hours (sampass), + ENTRY_VAL (old, NPF_HOURS), ENTRY_LEN (old, + NPF_HOURS))) + { + need_to_modify = True; + /* set_single_attribute will add 1 for len ... */ + set_single_attribute (obj, NPF_HOURS, + pdb_get_hours (sampass), + pdb_get_hours_len (sampass) - 1, + EN_MODIFIED); + } + } else { + const char *homedir, *dirdrive, *logon_script, *profile_path, + *workstations; + + *empty = '\0'; /* empty string */ + + set_single_attribute (obj, NPF_NAME, name, strlen (name), 0); + set_single_attribute (obj, NPF_UID, uid, strlen (uid), 0); + set_single_attribute (obj, NPF_USER_RID, user_rid, + strlen (user_rid), 0); + set_single_attribute (obj, NPF_SMB_GRPID, gid, strlen (gid), + 0); + set_single_attribute (obj, NPF_GROUP_RID, group_rid, + strlen (group_rid), 0); + set_single_attribute (obj, NPF_ACB, acb, strlen (acb), 0); + set_single_attribute (obj, NPF_LMPWD, smb_passwd, + strlen (smb_passwd), EN_CRYPT); + set_single_attribute (obj, NPF_NTPWD, smb_nt_passwd, + strlen (smb_nt_passwd), EN_CRYPT); + set_single_attribute (obj, NPF_LOGON_T, logon_t, + strlen (logon_t), 0); + set_single_attribute (obj, NPF_LOGOFF_T, logoff_t, + strlen (logoff_t), 0); + set_single_attribute (obj, NPF_KICK_T, kickoff_t, + strlen (kickoff_t), 0); + set_single_attribute (obj, NPF_PWDLSET_T, pwdlset_t, + strlen (pwdlset_t), 0); + set_single_attribute (obj, NPF_PWDCCHG_T, pwdlchg_t, + strlen (pwdlchg_t), 0); + set_single_attribute (obj, NPF_PWDMCHG_T, pwdmchg_t, + strlen (pwdmchg_t), 0); + set_single_attribute (obj, NPF_FULL_NAME, + full_name, strlen (full_name), 0); + + if (!(homedir = pdb_get_homedir (sampass))) + homedir = empty; + + set_single_attribute (obj, NPF_HOME_DIR, + homedir, strlen (homedir), 0); + + if (!(dirdrive = pdb_get_dir_drive (sampass))) + dirdrive = empty; + + set_single_attribute (obj, NPF_DIR_DRIVE, + dirdrive, strlen (dirdrive), 0); + + if (!(logon_script = pdb_get_logon_script (sampass))) + logon_script = empty; + + set_single_attribute (obj, NPF_LOGON_SCRIPT, + logon_script, strlen (logon_script), 0); + + if (!(profile_path = pdb_get_profile_path (sampass))) + profile_path = empty; + + set_single_attribute (obj, NPF_PROFILE_PATH, + profile_path, strlen (profile_path), 0); + + set_single_attribute (obj, NPF_ACCT_DESC, + acct_desc, strlen (acct_desc), 0); + + if (!(workstations = pdb_get_workstations (sampass))) + workstations = empty; + + set_single_attribute (obj, NPF_WORKSTATIONS, + workstations, strlen (workstations), 0); + + /* set_single_attribute will add 1 for len ... */ + set_single_attribute (obj, NPF_HOURS, + pdb_get_hours (sampass), + pdb_get_hours_len (sampass) - 1, 0); + } + + return need_to_modify; +} + +/*************************************************************** + calls nis_list, returns results. + ****************************************************************/ +static nis_result *nisp_get_nis_list (const char *nisname, unsigned int flags) +{ + nis_result *result; + int i; + + if (!flags) + flags = FOLLOW_LINKS | FOLLOW_PATH | EXPAND_NAME | + HARD_LOOKUP; + + for (i = 0; i < 2; i++) { + alarm (60); /* hopefully ok for long searches */ + result = nis_list (nisname, flags, NULL, NULL); + + alarm (0); + CatchSignal (SIGALRM, SIGNAL_CAST SIG_DFL); + + if (!(flags & MASTER_ONLY) && NIS_RES_NUMOBJ (result) <= 0) { + /* nis replicas are not in sync perhaps? + * this can happen, if account was just added. + */ + DEBUG (10, ("will try master only\n")); + nis_freeresult (result); + flags |= MASTER_ONLY; + } else + break; + } + return result; +} + +static void free_private_data(void **vp) +{ + struct nisplus_private_info **private = (struct nisplus_private_info **)vp; + + if ((*private)->result) { + nis_freeresult ((*private)->result); + } + + free(*private); + + /* No need to free any further, as it is talloc()ed */ +} + +NTSTATUS pdb_init_nisplussam (PDB_CONTEXT * pdb_context, + PDB_METHODS ** pdb_method, const char *location) +{ + NTSTATUS nt_status; + struct nisplus_private_info *private = malloc (sizeof (struct nisplus_private_info)); + + ZERO_STRUCT(private); + p->location = talloc_strdup(pdb_context->mem_ctx, location); + + if (!NT_STATUS_IS_OK + (nt_status = + make_pdb_methods (pdb_context->mem_ctx, pdb_method))) { + return nt_status; + } + + (*pdb_method)->name = "nisplussam"; + + /* Functions your pdb module doesn't provide should be set + * to NULL */ + + (*pdb_method)->setsampwent = nisplussam_setsampwent; + (*pdb_method)->endsampwent = nisplussam_endsampwent; + (*pdb_method)->getsampwent = nisplussam_getsampwent; + (*pdb_method)->getsampwnam = nisplussam_getsampwnam; + (*pdb_method)->getsampwsid = nisplussam_getsampwsid; + (*pdb_method)->add_sam_account = nisplussam_add_sam_account; + (*pdb_method)->update_sam_account = nisplussam_update_sam_account; + (*pdb_method)->delete_sam_account = nisplussam_delete_sam_account; + (*pdb_method)->free_private_data = free_private_data; + (*pdb_method)->private_data = private; + + return NT_STATUS_OK; +} + +#else +NTSTATUS pdb_init_nisplussam (PDB_CONTEXT * c, PDB_METHODS ** m, + const char *l) +{ + DEBUG (0, ("nisplus sam not compiled in!\n")); + return NT_STATUS_UNSUCCESSFUL; +} +#endif /* WITH_NISPLUS_SAM */ diff --git a/source4/passdb/pdb_smbpasswd.c b/source4/passdb/pdb_smbpasswd.c new file mode 100644 index 0000000000..6f8c8a6fcc --- /dev/null +++ b/source4/passdb/pdb_smbpasswd.c @@ -0,0 +1,1581 @@ +/* + * 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 + * 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 +{ + BOOL smb_userid_set; /* this is actually the unix uid_t */ + 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; + + BOOL permit_non_unix_accounts; + + uint32 low_nua_userid; + uint32 high_nua_userid; + +}; + +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) { + 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 */ + if(fchmod(fileno(fp), S_IRUSR|S_IWUSR) == -1) { + 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 (!strncasecmp((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 */ + + /* Account not in /etc/passwd hack!!! */ + if (!newpwd->smb_userid_set) { + if (!smbpasswd_state->permit_non_unix_accounts) { + DEBUG(0, ("add_smbfilepwd_entry: cannot add account %s without unix identity\n", newpwd->smb_name)); + endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth)); + return False; + } + + if (max_found_uid < smbpasswd_state->low_nua_userid) { + newpwd->smb_userid = smbpasswd_state->low_nua_userid; + newpwd->smb_userid_set = True; + } else if (max_found_uid >= smbpasswd_state->high_nua_userid) { + DEBUG(0, ("add_smbfilepwd_entry: cannot add machine %s, no uids are free! \n", newpwd->smb_name)); + endsmbfilepwent(fp, &(smbpasswd_state->pw_file_lock_depth)); + return False; + } else { + newpwd->smb_userid = max_found_uid + 1; + newpwd->smb_userid_set = True; + } + } + + + /* 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)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) +{ + uid_t uid; + + if (sampass == NULL) + return False; + + ZERO_STRUCTP(smb_pw); + + if (!IS_SAM_UNIX_USER(sampass)) { + smb_pw->smb_userid_set = False; + DEBUG(5,("build_smb_pass: storing user without a UNIX uid or gid. \n")); + } else { + uint32 rid = pdb_get_user_rid(sampass); + smb_pw->smb_userid_set = True; + uid = pdb_get_uid(sampass); + + /* If the user specified a RID, make sure its able to be both stored and retreived */ + if (rid && rid != DOMAIN_USER_RID_GUEST && uid != fallback_pdb_user_rid_to_uid(rid)) { + DEBUG(0,("build_sam_pass: Failing attempt to store user with non-uid based user RID. \n")); + return False; + } + + smb_pw->smb_userid=uid; + } + + 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); + +#if 0 + /* + * ifdef'out by JFM on 11/29/2001. + * this assertion is no longer valid + * and I don't understand the goal + * and doing the same thing with the group mapping code + * is hairy ! + * + * We just have the RID, in which SID is it valid ? + * our domain SID ? well known SID ? local SID ? + */ + + if (gid != pdb_group_rid_to_gid(pdb_get_group_rid(sampass))) { + DEBUG(0,("build_sam_pass: Failing attempt to store user with non-gid based primary group RID. \n")); + DEBUG(0,("build_sam_pass: %d %d %d. \n", *gid, pdb_group_rid_to_gid(pdb_get_group_rid(sampass)), pdb_get_group_rid(sampass))); + return False; + } +#endif + + 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; + } + + pwfile = getpwnam_alloc(pw_buf->smb_name); + if (pwfile == NULL) { + if ((smbpasswd_state->permit_non_unix_accounts) + && (pw_buf->smb_userid >= smbpasswd_state->low_nua_userid) + && (pw_buf->smb_userid <= smbpasswd_state->high_nua_userid)) { + + pdb_set_user_sid_from_rid(sam_pass, fallback_pdb_uid_to_user_rid (pw_buf->smb_userid), PDB_SET); + + /* lkclXXXX this is OBSERVED behaviour by NT PDCs, enforced here. + + This was down the bottom for machines, but it looks pretty good as + a general default for non-unix users. --abartlet 2002-01-08 + */ + pdb_set_group_sid_from_rid (sam_pass, DOMAIN_GROUP_RID_USERS, PDB_SET); + pdb_set_username (sam_pass, pw_buf->smb_name, PDB_SET); + pdb_set_domain (sam_pass, lp_workgroup(), PDB_DEFAULT); + + } else { + 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; + } + } else { + if (!NT_STATUS_IS_OK(pdb_fill_sam_pw(sam_pass, pwfile))) { + return False; + } + + passwd_free(&pwfile); + } + + 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); + +#if 0 /* JERRY */ + /* the smbpasswd format doesn't have a must change time field, so + we can't get this right. The best we can do is to set this to + some time in the future. 21 days seems as reasonable as any other value :) + */ + pdb_set_pass_must_change_time (sam_pass, pw_buf->pass_last_set_time + MAX_PASSWORD_AGE, PDB_DEFAULT); +#endif + 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 */ +} + + +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_init_smbpasswd_nua(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 = pdb_init_smbpasswd(pdb_context, pdb_method, location))) { + return nt_status; + } + + (*pdb_method)->name = "smbpasswd_nua"; + + privates = (*pdb_method)->private_data; + + privates->permit_non_unix_accounts = True; + + if (!lp_non_unix_account_range(&privates->low_nua_userid, &privates->high_nua_userid)) { + DEBUG(0, ("cannot use smbpasswd_nua without 'non unix account range' in smb.conf!\n")); + return NT_STATUS_UNSUCCESSFUL; + } + + return NT_STATUS_OK; +} diff --git a/source4/passdb/pdb_tdb.c b/source4/passdb/pdb_tdb.c new file mode 100644 index 0000000000..c48c9567b1 --- /dev/null +++ b/source4/passdb/pdb_tdb.c @@ -0,0 +1,1007 @@ +/* + * 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 + +#ifdef WITH_TDB_SAM + +#define PDB_VERSION "20010830" +#define PASSDB_FILE_NAME "passdb.tdb" +#define TDB_FORMAT_STRING "ddddddBBBBBBBBBBBBddBBwdwdBdd" +#define USERPREFIX "USER_" +#define RIDPREFIX "RID_" + +struct tdbsam_privates { + TDB_CONTEXT *passwd_tdb; + TDB_DATA key; + + /* retrive-once info */ + const char *tdbsam_location; + + BOOL permit_non_unix_accounts; + + BOOL algorithmic_rids; + + uint32 low_nua_rid; + uint32 high_nua_rid; +}; + +/********************************************************************** + Intialize a SAM_ACCOUNT struct from a BYTE buffer of size len + *********************************************************************/ + +static BOOL init_sam_from_buffer (struct tdbsam_privates *tdb_state, + 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, unknown_3, hours_len, unknown_5, unknown_6; + uint16 acct_ctrl, logon_divs; + uint8 *hours; + static uint8 *lm_pw_ptr, *nt_pw_ptr; + uint32 len = 0; + uint32 lm_pw_len, nt_pw_len, hourslen; + BOOL ret = True; + struct passwd *pw; + uid_t uid = -1; + gid_t gid = -1; /* This is what standard sub advanced expects if no gid is known */ + + 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 (buf, buflen, TDB_FORMAT_STRING, + &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, + &unknown_3, + &logon_divs, + &hours_len, + &hourslen, &hours, + &unknown_5, + &unknown_6); + + if (len == -1) { + ret = False; + goto done; + } + + /* validate the account and fill in UNIX uid and gid. Standard + * getpwnam() is used instead of Get_Pwnam() as we do not need + * to try case permutations + */ + if (!username || !(pw = getpwnam_alloc(username))) { + if (!(tdb_state->permit_non_unix_accounts)) { + DEBUG(0,("tdbsam: getpwnam_alloc(%s) return NULL. User does not exist!\n", username)); + ret = False; + goto done; + } + } + + if (pw) { + uid = pw->pw_uid; + gid = pw->pw_gid; + + pdb_set_unix_homedir(sampass, pw->pw_dir, PDB_SET); + + passwd_free(&pw); + + pdb_set_uid(sampass, uid, PDB_SET); + pdb_set_gid(sampass, gid, PDB_SET); + } + + 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_specified(sampass->mem_ctx, + lp_logon_home(), + username, domain, + uid, gid), + PDB_DEFAULT); + } + + if (dir_drive) + pdb_set_dir_drive(sampass, dir_drive, PDB_SET); + else { + pdb_set_dir_drive(sampass, + talloc_sub_specified(sampass->mem_ctx, + lp_logon_drive(), + username, domain, + uid, gid), + PDB_DEFAULT); + } + + if (logon_script) + pdb_set_logon_script(sampass, logon_script, PDB_SET); + else { + pdb_set_logon_script(sampass, + talloc_sub_specified(sampass->mem_ctx, + lp_logon_script(), + username, domain, + uid, gid), + PDB_DEFAULT); + } + + if (profile_path) { + pdb_set_profile_path(sampass, profile_path, PDB_SET); + } else { + pdb_set_profile_path(sampass, + talloc_sub_specified(sampass->mem_ctx, + lp_logon_path(), + username, domain, + uid, gid), + 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_unknown_3(sampass, unknown_3, PDB_SET); + pdb_set_hours_len(sampass, hours_len, PDB_SET); + pdb_set_unknown_5(sampass, unknown_5, 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); + + return ret; +} + +/********************************************************************** + Intialize a BYTE buffer from a SAM_ACCOUNT struct + *********************************************************************/ +static uint32 init_buffer_from_sam (struct tdbsam_privates *tdb_state, + uint8 **buf, const SAM_ACCOUNT *sampass) +{ + 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, + 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), + pdb_get_unknown_3(sampass), + pdb_get_logon_divs(sampass), + pdb_get_hours_len(sampass), + MAX_HOURS_LEN, pdb_get_hours(sampass), + pdb_get_unknown_5(sampass), + pdb_get_unknown_6(sampass)); + + + /* 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(*buf, len, TDB_FORMAT_STRING, + 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), + pdb_get_unknown_3(sampass), + pdb_get_logon_divs(sampass), + pdb_get_hours_len(sampass), + MAX_HOURS_LEN, pdb_get_hours(sampass), + pdb_get_unknown_5(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 (%d) != len (%d) in tdb_pack operations!\n", + buflen, len)); + /* error */ + SAFE_FREE (*buf); + return (-1); + } + + return (buflen); +} + +/*************************************************************** + Open the TDB passwd database for SAM account enumeration. +****************************************************************/ + +static NTSTATUS tdbsam_setsampwent(struct pdb_methods *my_methods, BOOL update) +{ + struct tdbsam_privates *tdb_state = (struct tdbsam_privates *)my_methods->private_data; + + /* Open tdb passwd */ + if (!(tdb_state->passwd_tdb = tdb_open_log(tdb_state->tdbsam_location, 0, TDB_DEFAULT, update?(O_RDWR|O_CREAT):O_RDONLY, 0600))) + { + DEBUG(0, ("Unable to open/create TDB passwd\n")); + return NT_STATUS_UNSUCCESSFUL; + } + + tdb_state->key = tdb_firstkey(tdb_state->passwd_tdb); + + return NT_STATUS_OK; +} + +static void close_tdb(struct tdbsam_privates *tdb_state) +{ + if (tdb_state->passwd_tdb) { + tdb_close(tdb_state->passwd_tdb); + tdb_state->passwd_tdb = NULL; + } +} + +/*************************************************************** + 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; + close_tdb(tdb_state); + + 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; + const char *prefix = USERPREFIX; + int prefixlen = strlen (prefix); + + + if (user==NULL) { + DEBUG(0,("pdb_get_sampwent: SAM_ACCOUNT is NULL.\n")); + return nt_status; + } + + /* skip all non-USER entries (eg. RIDs) */ + while ((tdb_state->key.dsize != 0) && (strncmp(tdb_state->key.dptr, prefix, prefixlen))) + /* increment to next in line */ + tdb_state->key = tdb_nextkey(tdb_state->passwd_tdb, tdb_state->key); + + /* do we have an valid iteration pointer? */ + if(tdb_state->passwd_tdb == NULL) { + DEBUG(0,("pdb_get_sampwent: Bad TDB Context pointer.\n")); + return nt_status; + } + + data = tdb_fetch(tdb_state->passwd_tdb, tdb_state->key); + if (!data.dptr) { + DEBUG(5,("pdb_getsampwent: database entry not found.\n")); + return nt_status; + } + + /* unpack the buffer */ + if (!init_sam_from_buffer(tdb_state, user, data.dptr, data.dsize)) { + DEBUG(0,("pdb_getsampwent: Bad SAM_ACCOUNT entry returned from TDB!\n")); + SAFE_FREE(data.dptr); + return nt_status; + } + SAFE_FREE(data.dptr); + + /* increment to next in line */ + tdb_state->key = tdb_nextkey(tdb_state->passwd_tdb, tdb_state->key); + + 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==NULL) { + DEBUG(0,("pdb_getsampwnam: SAM_ACCOUNT is NULL.\n")); + return nt_status; + } + + /* Data is stored in all lower-case */ + unix_strlower(sname, -1, name, sizeof(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 = tdb_open_log(tdb_state->tdbsam_location, 0, TDB_DEFAULT, O_RDONLY, 0600))) { + 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(tdb_state, user, 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 = tdb_open_log(tdb_state->tdbsam_location, 0, TDB_DEFAULT, O_RDONLY, 0600))) { + 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; + + unix_strlower(pdb_get_username(sam_pass), -1, name, sizeof(name)); + + /* open the TDB */ + if (!(pwd_tdb = tdb_open_log(tdb_state->tdbsam_location, 0, TDB_DEFAULT, O_RDWR, 0600))) { + 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; + BOOL tdb_ret; + + /* 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 = tdb_open_log(tdb_state->tdbsam_location, 0, TDB_DEFAULT, O_RDWR | O_CREAT, 0600); + if (!pwd_tdb) + { + DEBUG(0, ("tdb_update_sam: Unable to open TDB passwd (%s)!\n", tdb_state->tdbsam_location)); + return False; + } + + /* if flag == TDB_INSERT then make up a new RID else throw an error. */ + if (!(user_rid = pdb_get_user_rid(newpwd))) { + if (flag & TDB_INSERT) { + if (IS_SAM_UNIX_USER(newpwd)) { + if (tdb_state->algorithmic_rids) { + user_rid = fallback_pdb_uid_to_user_rid(pdb_get_uid(newpwd)); + } else { + user_rid = BASE_RID; + tdb_ret = tdb_change_uint32_atomic(pwd_tdb, "RID_COUNTER", &user_rid, RID_MULTIPLIER); + if (!tdb_ret) { + ret = False; + goto done; + } + } + pdb_set_user_sid_from_rid(newpwd, user_rid, PDB_CHANGED); + } else { + user_rid = tdb_state->low_nua_rid; + tdb_ret = tdb_change_uint32_atomic(pwd_tdb, "NUA_RID_COUNTER", &user_rid, RID_MULTIPLIER); + if (!tdb_ret) { + ret = False; + goto done; + } + if (user_rid > tdb_state->high_nua_rid) { + DEBUG(0, ("tdbsam: no NUA rids available, cannot add user %s!\n", pdb_get_username(newpwd))); + ret = False; + goto done; + } + pdb_set_user_sid_from_rid(newpwd, user_rid, PDB_CHANGED); + } + } else { + DEBUG (0,("tdb_update_sam: Failing to store a SAM_ACCOUNT for [%s] without a RID\n",pdb_get_username(newpwd))); + ret = False; + goto done; + } + } + + if (!pdb_get_group_rid(newpwd)) { + if (flag & TDB_INSERT) { + if (!tdb_state->permit_non_unix_accounts) { + 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; + } else { + /* This seems like a good default choice for non-unix users */ + pdb_set_group_sid_from_rid(newpwd, DOMAIN_GROUP_RID_USERS, PDB_DEFAULT); + } + } else { + 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; + } + } + + /* copy the SAM_ACCOUNT struct into a BYTE buffer for storage */ + if ((data.dsize=init_buffer_from_sam (tdb_state, &buf, newpwd)) == -1) { + DEBUG(0,("tdb_update_sam: ERROR - Unable to copy SAM_ACCOUNT info BYTE buffer!\n")); + ret = False; + goto done; + } + data.dptr = buf; + + unix_strlower(pdb_get_username(newpwd), -1, name, sizeof(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 = sizeof(fstring); + 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; + close_tdb(*tdb_state); + *tdb_state = NULL; + + /* No need to free any further, as it is talloc()ed */ +} + + +NTSTATUS pdb_init_tdbsam(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location) +{ + NTSTATUS nt_status; + struct tdbsam_privates *tdb_state; + +#if 0 /* when made a module use this */ + tdbsam_debug_level = debug_add_class("tdbsam"); + if(tdbsam_debug_level == -1) { + tdbsam_debug_level = DBGC_ALL; + DEBUG(0, ("tdbsam: Couldn't register custom debugging class!\n")); + } +#endif + + 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; + + 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); + } + + tdb_state->algorithmic_rids = True; + + (*pdb_method)->private_data = tdb_state; + + (*pdb_method)->free_private_data = free_private_data; + + return NT_STATUS_OK; +} + +NTSTATUS pdb_init_tdbsam_nua(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location) +{ + NTSTATUS nt_status; + struct tdbsam_privates *tdb_state; + uint32 low_nua_uid, high_nua_uid; + + if (!NT_STATUS_IS_OK(nt_status = pdb_init_tdbsam(pdb_context, pdb_method, location))) { + return nt_status; + } + + (*pdb_method)->name = "tdbsam_nua"; + + tdb_state = (*pdb_method)->private_data; + + tdb_state->permit_non_unix_accounts = True; + + if (!lp_non_unix_account_range(&low_nua_uid, &high_nua_uid)) { + DEBUG(0, ("cannot use tdbsam_nua without 'non unix account range' in smb.conf!\n")); + return NT_STATUS_UNSUCCESSFUL; + } + + tdb_state->low_nua_rid=fallback_pdb_uid_to_user_rid(low_nua_uid); + + tdb_state->high_nua_rid=fallback_pdb_uid_to_user_rid(high_nua_uid); + + return NT_STATUS_OK; +} + + +#else + +NTSTATUS pdb_init_tdbsam(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location) +{ + DEBUG(0, ("tdbsam not compiled in!\n")); + return NT_STATUS_UNSUCCESSFUL; +} + +NTSTATUS pdb_init_tdbsam_nua(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location) +{ + DEBUG(0, ("tdbsam_nua not compiled in!\n")); + return NT_STATUS_UNSUCCESSFUL; +} + + +#endif diff --git a/source4/passdb/pdb_unix.c b/source4/passdb/pdb_unix.c new file mode 100644 index 0000000000..b42843d802 --- /dev/null +++ b/source4/passdb/pdb_unix.c @@ -0,0 +1,110 @@ +/* + * Unix password backend for samba + * 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" + +/****************************************************************** + Lookup a name in the SAM database + ******************************************************************/ + +static NTSTATUS unixsam_getsampwnam (struct pdb_methods *methods, SAM_ACCOUNT *user, const char *sname) +{ + struct passwd *pass; + if (!methods) { + DEBUG(0,("invalid methods\n")); + return NT_STATUS_UNSUCCESSFUL; + } + if (!sname) { + DEBUG(0,("invalid name specified")); + return NT_STATUS_UNSUCCESSFUL; + } + pass = Get_Pwnam(sname); + + return pdb_fill_sam_pw(user, pass); +} + + +/*************************************************************************** + Search by rid + **************************************************************************/ + +static NTSTATUS unixsam_getsampwrid (struct pdb_methods *methods, + SAM_ACCOUNT *user, uint32 rid) +{ + NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL; + struct passwd *pass = NULL; + const char *guest_account = lp_guestaccount(); + if (!(guest_account && *guest_account)) { + DEBUG(1, ("NULL guest account!?!?\n")); + return nt_status; + } + + if (!methods) { + DEBUG(0,("invalid methods\n")); + return nt_status; + } + + if (rid == DOMAIN_USER_RID_GUEST) { + pass = getpwnam_alloc(guest_account); + if (!pass) { + DEBUG(1, ("guest account %s does not seem to exist...\n", guest_account)); + return nt_status; + } + } else if (pdb_rid_is_user(rid)) { + pass = getpwuid_alloc(fallback_pdb_user_rid_to_uid (rid)); + } + + if (pass == NULL) { + return nt_status; + } + + nt_status = pdb_fill_sam_pw(user, pass); + passwd_free(&pass); + + return nt_status; +} + +static NTSTATUS unixsam_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 unixsam_getsampwrid(my_methods, user, rid); +} + +NTSTATUS pdb_init_unixsam(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 = "unixsam"; + (*pdb_method)->getsampwnam = unixsam_getsampwnam; + (*pdb_method)->getsampwsid = unixsam_getsampwsid; + + /* There's not very much to initialise here */ + return NT_STATUS_OK; +} diff --git a/source4/passdb/privileges.c b/source4/passdb/privileges.c new file mode 100644 index 0000000000..c90bc47d31 --- /dev/null +++ b/source4/passdb/privileges.c @@ -0,0 +1,349 @@ +/* + * 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) +{ + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_init("privilege_init talloc"); + if (!mem_ctx) { + DEBUG(0,("No memory to open privilege database\n")); + return False; + } + tdb = tdb_open_log(lock_path(mem_ctx, "privilege.tdb"), 0, TDB_DEFAULT, + O_RDWR|O_CREAT, 0600); + talloc_destroy(mem_ctx); + 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_by_string(tdb, right); + if (!data.dptr) { + *count = 0; + *sids = NULL; + return NT_STATUS_OK; + } + + /* count them */ + for (i=0, p=data.dptr; p 1) { + memmove(¤t_sids[i], ¤t_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;imod_time; + memcpy(ret_pwd, pass->hash, 16); + SAFE_FREE(pass); + 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; + + /* fetching trusted domain password structure */ + if (!(pass = secrets_fetch(trustdom_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; + } + + /* 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 */ + memcpy(&sid, &(pass->domain_sid), sizeof(sid)); + + SAFE_FREE(pass); + + return True; +} + +/************************************************************************ + Routine to set the trust account password for a domain. +************************************************************************/ + +BOOL secrets_store_trust_account_password(const char *domain, uint8 new_pwd[16]) +{ + struct machine_acct_pass pass; + + pass.mod_time = time(NULL); + memcpy(pass.hash, new_pwd, 16); + + return secrets_store(trust_keystr(domain), (void *)&pass, sizeof(pass)); +} + +/** + * Routine to set 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) +{ + 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 */ + memcpy(&(pass.domain_sid), &sid, sizeof(sid)); + + return secrets_store(trustdom_keystr(domain), (void *)&pass, sizeof(pass)); +} + +/************************************************************************ + 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) +{ + char *key; + BOOL ret; + asprintf(&key, "%s/%s", SECRETS_MACHINE_PASSWORD, lp_workgroup()); + strupper(key); + ret = secrets_store(key, pass, strlen(pass)+1); + 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(void) +{ + char *key; + char *ret; + asprintf(&key, "%s/%s", SECRETS_MACHINE_PASSWORD, lp_workgroup()); + strupper(key); + ret = (char *)secrets_fetch(key, NULL); + 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)); +} + + +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; + fstring dom_name; + struct trusted_dom_pass *pass; + NTSTATUS status; + + if (!secrets_init()) return NT_STATUS_ACCESS_DENIED; + + *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 sectrets 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; + } + + pass = secrets_fetch(secrets_key, &size); + + if (size != sizeof(*pass)) { + DEBUG(2, ("Secrets record %s is invalid!\n", secrets_key)); + SAFE_FREE(pass); + 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_talloc(ctx, &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 */ + SAFE_FREE(pass); + + 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++; + + /* free returned tdb record */ + SAFE_FREE(pass); + } + + 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, size_t *p_ref_count) +{ + size_t ref_count = *p_ref_count; + int ret = 0; + + if (!message_init()) + return False; + + if (ref_count == 0) { + ret = tdb_lock_bystring(tdb, name, timeout); + if (ret == 0) + DEBUG(10,("secrets_named_mutex: got mutex for %s\n", name )); + } + + if (ret == 0) { + *p_ref_count = ++ref_count; + DEBUG(10,("secrets_named_mutex: ref_count for mutex %s = %u\n", name, (unsigned int)ref_count )); + } + return (ret == 0); +} + +/******************************************************************************* + Unlock a named mutex. +*******************************************************************************/ + +void secrets_named_mutex_release(const char *name, size_t *p_ref_count) +{ + size_t ref_count = *p_ref_count; + + SMB_ASSERT(ref_count != 0); + + if (ref_count == 1) { + tdb_unlock_bystring(tdb, name); + DEBUG(10,("secrets_named_mutex: released mutex for %s\n", name )); + } + + *p_ref_count = --ref_count; + DEBUG(10,("secrets_named_mutex_release: ref_count for mutex %s = %u\n", name, (unsigned int)ref_count )); +} + diff --git a/source4/passdb/util_sam_sid.c b/source4/passdb/util_sam_sid.c new file mode 100644 index 0000000000..e18386801c --- /dev/null +++ b/source4/passdb/util_sam_sid.c @@ -0,0 +1,303 @@ +/* + 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_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" }, + { 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(lp_netbios_name()); + sid_name_map[i].known_users = NULL; + i++; + } else { + sid_name_map[i].sid = get_global_sam_sid(); + sid_name_map[i].name = strdup(lp_netbios_name()); + 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, lp_netbios_name()); + 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) == 0) { + 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; +} diff --git a/source4/po/de.msg b/source4/po/de.msg new file mode 100644 index 0000000000..6a8da43ae3 --- /dev/null +++ b/source4/po/de.msg @@ -0,0 +1,1707 @@ +# German messages for international release of SWAT. +# Copyright (C) 2001 Andreas Moroder + +msgid "" +msgstr "" +"Project-Id-Version: i18n_swat \n" +"POT-Creation-Date: 2001-10-27 14:05+0100\n" +"PO-Revision-Date: 2000-02-08 14:45+0100\n" +"Last-Translator: Andreas Moroder" +"Language-Team: (Samba Team) \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=US-ASCII\n" +"Content-Transfer-Encoding: \n" + +#: web/swat.c:120 +#, c-format +msgid "ERROR: Can't open %s\n" +msgstr "ERRORE: Kann %s nicht öffnen\n" + +#. +#. str = stripspace(parm->label); +#. strlower (str); //monyo +#. d_printf("%s       %s", +#. str, _("Help"), parm->label); +#. +#: web/swat.c:211 +msgid "Help" +msgstr "Hilfe" + +#: web/swat.c:217 web/swat.c:231 web/swat.c:246 web/swat.c:254 web/swat.c:263 +#: web/swat.c:272 web/swat.c:278 web/swat.c:284 web/swat.c:297 +msgid "Set Default" +msgstr "Setze Standardwerte" + +#: web/swat.c:502 +#, c-format +msgid "Logged in as %s

\n" +msgstr "Verbunden als %s

\n" + +#: 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:512 +msgid "Status" +msgstr "Status" + +#: web/swat.c:513 +msgid "View Config" +msgstr "Zeige Konfiguration" + +#: web/swat.c:515 +msgid "Password Management" +msgstr "Passwortverwaltung" + +#: web/swat.c:539 +msgid "Current Config" +msgstr "Aktuelle Konfiguration" + +#: web/swat.c:543 +msgid "Normal View" +msgstr "Normale Ansich" + +#: web/swat.c:545 +msgid "Full View" +msgstr "Komplette Ansicht" + +#: web/swat.c:561 +msgid "Global Variables" +msgstr "Globale Variablen" + +#: web/swat.c:575 web/swat.c:671 web/swat.c:1014 +msgid "Commit Changes" +msgstr "Speichere Änderungen" + +#: web/swat.c:579 web/swat.c:674 web/swat.c:1016 +msgid "Reset Values" +msgstr "Setze Werte zurück" + +#: web/swat.c:581 web/swat.c:676 web/swat.c:1018 +msgid "Advanced View" +msgstr "Erweiterte Ansicht" + +#: web/swat.c:583 web/swat.c:678 web/swat.c:1020 +msgid "Basic View" +msgstr "Basis Ansicht" + +#: web/swat.c:613 +msgid "Share Parameters" +msgstr "Parameter der Freigabe" + +#: web/swat.c:642 +msgid "Choose Share" +msgstr "Wähle Freigabe" + +#: web/swat.c:656 +msgid "Delete Share" +msgstr "Lösche Freigabe" + +#: web/swat.c:663 +msgid "Create Share" +msgstr "Erstelle Freigabe" + +#: web/swat.c:708 +msgid "password change in demo mode rejected\n" +msgstr "Änderung des Passworts im Demo modus nicht aktiv" + +#: web/swat.c:747 +msgid " Must specify \"User Name\" \n" +msgstr " \"Benutzername\" muss angegeben werden \n" + +#: web/swat.c:763 +msgid " Must specify \"Old Password\" \n" +msgstr " \"Altes Passwort\" muß angegeben werden \n" + +#: web/swat.c:769 +msgid " Must specify \"Remote Machine\" \n" +msgstr " \"Remote Maschine\" muß angegeben werden \n" + +#: web/swat.c:776 +msgid " Must specify \"New, and Re-typed Passwords\" \n" +msgstr " "Neues/Bestätige Passwort" muß angegeben werden \n" + +#: web/swat.c:782 +msgid " Re-typed password didn't match new password\n" +msgstr " Das bestätigte Passwort stimmt nicht mit dem neuen Passwort überein\n" + +#: web/swat.c:812 +#, c-format +msgid " The passwd for '%s' has been changed. \n" +msgstr " Das Passwort für '%s' wurde geändert. \n" + +#: web/swat.c:814 +#, c-format +msgid " The passwd for '%s' has NOT been changed. \n" +msgstr " Das Passwort für '%s' wurde nicht geändert. \n" + +#: web/swat.c:838 +msgid "Server Password Management" +msgstr "Verwaltung des Server Passwortes" + +#. +#. * Create all the dialog boxes for data collection +#. +#: web/swat.c:847 web/swat.c:894 +msgid " User Name : " +msgstr " Benutzername : " + +#: web/swat.c:850 web/swat.c:896 +msgid " Old Password : " +msgstr " Altes Passwort : " + +#: web/swat.c:853 web/swat.c:898 +msgid " New Password : " +msgstr " Neues Passwort : " + +#: web/swat.c:855 web/swat.c:900 +msgid " Re-type New Password : " +msgstr " Bestätige neues Passwort : " + +#: web/swat.c:863 web/swat.c:911 +msgid "Change Password" +msgstr "Ändere Passwort" + +#: web/swat.c:866 +msgid "Add New User" +msgstr "Füge Benutzer hinzu" + +#: web/swat.c:868 +msgid "Delete User" +msgstr "Lösche Benutzer" + +#: web/swat.c:870 +msgid "Disable User" +msgstr "Desaktiviere Benutzer" + +#: web/swat.c:872 +msgid "Enable User" +msgstr "Aktiviere Benutzer" + +#: web/swat.c:885 +msgid "Client/Server Password Management" +msgstr "Client/Server Passwort Verwaltung" + +#: web/swat.c:902 +msgid " Remote Machine : " +msgstr " Remote Maschine : " + +#: web/swat.c:940 +msgid "Printer Parameters" +msgstr "Drucker Parameter" + +#: web/swat.c:942 +msgid "Important Note:" +msgstr "Wichtige Hinweise:" + +#: web/swat.c:943 +msgid "Printer names marked with [*] in the Choose Printer drop-down box " +msgstr "Mit [*] gekennzeichnete Druckername in der Druckerauswahlliste" + +#: web/swat.c:944 +msgid "are autoloaded printers from " +msgstr "wurde automatisch geladen von :" + +#: web/swat.c:945 +msgid "Printcap Name" +msgstr "Printcap Name" + +#: web/swat.c:946 +msgid "Attempting to delete these printers from SWAT will have no effect.\n" +msgstr "Der Versuch diese Drucker von SWAT aus zu löschen wird keine Auswirkung haben.\n" + +#: web/swat.c:980 +msgid "Choose Printer" +msgstr "Wähle Drucker" + +#: web/swat.c:999 +msgid "Delete Printer" +msgstr "Lösche Drucker" + +#: web/swat.c:1006 +msgid "Create Printer" +msgstr "Ersteller Drucker" + +#: web/statuspage.c:40 +msgid "DENY_NONE" +msgstr "" + +#: web/statuspage.c:41 +msgid "DENY_ALL " +msgstr "" + +#: web/statuspage.c:42 +msgid "DENY_DOS " +msgstr "" + +#: web/statuspage.c:43 +msgid "DENY_READ " +msgstr "" + +#: web/statuspage.c:44 +msgid "DENY_WRITE " +msgstr "" + +#: web/statuspage.c:50 +msgid "RDONLY " +msgstr "" + +#: web/statuspage.c:51 +msgid "WRONLY " +msgstr "" + +#: web/statuspage.c:52 +msgid "RDWR " +msgstr "" + +#: web/statuspage.c:60 +msgid "EXCLUSIVE+BATCH " +msgstr "" + +#: web/statuspage.c:62 +msgid "EXCLUSIVE " +msgstr "" + +#: web/statuspage.c:64 +msgid "BATCH " +msgstr "" + +#: web/statuspage.c:66 +msgid "LEVEL_II " +msgstr "" + +#: web/statuspage.c:68 +msgid "NONE " +msgstr "" + +#: web/statuspage.c:195 +msgid "Server Status" +msgstr "Server Status" + +#: web/statuspage.c:200 +msgid "Auto Refresh" +msgstr "Automatische Aktualisierung" + +#: web/statuspage.c:201 web/statuspage.c:206 +msgid "Refresh Interval: " +msgstr "Aktualisierungsintervall: " + +#: web/statuspage.c:205 +msgid "Stop Refreshing" +msgstr "Stop Aktualisierung" + +#: web/statuspage.c:220 +msgid "version:" +msgstr "Version:" + +#: web/statuspage.c:223 +msgid "smbd:" +msgstr "" + +#: web/statuspage.c:223 web/statuspage.c:235 +msgid "running" +msgstr "aktiv" + +#: web/statuspage.c:223 web/statuspage.c:235 +msgid "not running" +msgstr "inaktiv" + +#: web/statuspage.c:226 +msgid "Stop smbd" +msgstr "Stopp smbd" + +#: web/statuspage.c:228 +msgid "Start smbd" +msgstr "Start smbd" + +#: web/statuspage.c:230 +msgid "Restart smbd" +msgstr "Neustart smbd" + +#: web/statuspage.c:235 +msgid "nmbd:" +msgstr "" + +#: web/statuspage.c:238 +msgid "Stop nmbd" +msgstr "Stopp nmbd" + +#: web/statuspage.c:240 +msgid "Start nmbd" +msgstr "Start nmbd" + +#: web/statuspage.c:242 +msgid "Restart nmbd" +msgstr "Neustart nmbd" + +#: web/statuspage.c:249 +msgid "Active Connections" +msgstr "Aktive Verbindungen" + +#: web/statuspage.c:251 web/statuspage.c:264 web/statuspage.c:272 +msgid "PID" +msgstr "" + +#: web/statuspage.c:251 web/statuspage.c:264 +msgid "Client" +msgstr "" + +#: web/statuspage.c:251 +msgid "IP address" +msgstr "IP Adresse" + +#: web/statuspage.c:251 web/statuspage.c:264 web/statuspage.c:272 +msgid "Date" +msgstr "Datum" + +#: web/statuspage.c:253 +msgid "Kill" +msgstr "Kill" + +#: web/statuspage.c:261 +msgid "Active Shares" +msgstr "Aktive Freigaben" + +#: web/statuspage.c:264 +msgid "Share" +msgstr "Freigabe" + +#: web/statuspage.c:264 +msgid "User" +msgstr "Benutzer" + +#: web/statuspage.c:264 +msgid "Group" +msgstr "Gruppe" + +#: web/statuspage.c:270 +msgid "Open Files" +msgstr "Offene Dateien" + +#: web/statuspage.c:272 +msgid "Sharing" +msgstr "" + +#: web/statuspage.c:272 +msgid "R/W" +msgstr "" + +#: web/statuspage.c:272 +msgid "Oplock" +msgstr "" + +#: web/statuspage.c:272 +msgid "File" +msgstr "" + +#: param/loadparm.c:641 +msgid "Base Options" +msgstr "Basisoptionen" + +#: param/loadparm.c:643 +msgid "dos charset" +msgstr "dos Charakterset" + +#: param/loadparm.c:644 +msgid "unix charset" +msgstr "unix Charakterset" + +#: param/loadparm.c:645 +msgid "display charset" +msgstr "Anzeige Charakterset" + +#: param/loadparm.c:646 +msgid "comment" +msgstr "Kommentar" + +#: param/loadparm.c:647 +msgid "path" +msgstr "Pfad" + +#: param/loadparm.c:648 +msgid "directory" +msgstr "Verzeichnis" + +#: param/loadparm.c:649 +msgid "workgroup" +msgstr "Arbeitsgruppe" + +#: param/loadparm.c:650 +msgid "netbios name" +msgstr "netbios name" + +#: param/loadparm.c:651 +msgid "netbios aliases" +msgstr "netbios aliase" + +#: param/loadparm.c:652 +msgid "netbios scope" +msgstr "netbios scope" + +#: param/loadparm.c:653 +msgid "server string" +msgstr "server string" + +#: param/loadparm.c:654 +msgid "interfaces" +msgstr "Schnittstellen" + +#: param/loadparm.c:655 +msgid "bind interfaces only" +msgstr "verwende nur definierte Schnittstellen" + +#: param/loadparm.c:657 +msgid "Security Options" +msgstr "Sicherheitsoptionen" + +#: param/loadparm.c:659 +msgid "security" +msgstr "Sicherheit" + +#: param/loadparm.c:660 +msgid "encrypt passwords" +msgstr "Verschlüsselte Passwörter" + +#: param/loadparm.c:661 +msgid "update encrypted" +msgstr "update Verschlüsselte" + +#: param/loadparm.c:662 +msgid "allow trusted domains" +msgstr "Erlaube Vertrauneswürdige Domänen" + +#: param/loadparm.c:663 +msgid "alternate permissions" +msgstr "Alternative Berechtigungen" + +#: param/loadparm.c:664 +msgid "hosts equiv" +msgstr "" + +#: param/loadparm.c:665 +msgid "min passwd length" +msgstr "Min. Länge Passwort" + +#: param/loadparm.c:666 +msgid "min password length" +msgstr "Min. Länge Passwort" + +#: param/loadparm.c:667 +msgid "map to guest" +msgstr "Map nach Gast" + +#: param/loadparm.c:668 +msgid "null passwords" +msgstr "leere Passwörter" + +#: param/loadparm.c:669 +msgid "obey pam restrictions" +msgstr "Folge pam Einschränkungen" + +#: param/loadparm.c:670 +msgid "password server" +msgstr "Serverpasswort" + +#: param/loadparm.c:671 +msgid "smb passwd file" +msgstr "smb passwd Datei" + +#: param/loadparm.c:672 +msgid "private dir" +msgstr "Privates Verzeichnis" + +#: param/loadparm.c:673 +msgid "passdb module path" +msgstr "Pfad passdb Modul" + +#: param/loadparm.c:674 +msgid "root directory" +msgstr "" + +#: param/loadparm.c:675 +msgid "root dir" +msgstr "" + +#: param/loadparm.c:676 +msgid "root" +msgstr "" + +#: param/loadparm.c:678 +msgid "pam password change" +msgstr "pam Passwortänderung" + +#: param/loadparm.c:679 +msgid "passwd program" +msgstr "" + +#: param/loadparm.c:680 +msgid "passwd chat" +msgstr "" + +#: param/loadparm.c:681 +msgid "passwd chat debug" +msgstr "" + +#: param/loadparm.c:682 +msgid "username map" +msgstr "username map" + +#: param/loadparm.c:683 +msgid "password level" +msgstr "Stufe Passwort " + +#: param/loadparm.c:684 +msgid "username level" +msgstr "Stufe Benutzer" + +#: param/loadparm.c:685 +msgid "unix password sync" +msgstr "Synchronisiere unix Passwort " + +#: param/loadparm.c:686 +msgid "restrict anonymous" +msgstr "Beschränke anonymus" + +#: param/loadparm.c:687 +msgid "lanman auth" +msgstr "" + +#: param/loadparm.c:688 +msgid "ntlm auth" +msgstr "" + +#: param/loadparm.c:689 +msgid "plaintext to smbpasswd" +msgstr "plaintext to smbpasswd" + +#: param/loadparm.c:690 +msgid "use rhosts" +msgstr "use rhosts" + +#: param/loadparm.c:692 +msgid "username" +msgstr "Benutzername" + +#: param/loadparm.c:693 +msgid "user" +msgstr "Benutzer" + +#: param/loadparm.c:694 +msgid "users" +msgstr "Benutzer" + +#: param/loadparm.c:696 +msgid "guest account" +msgstr "Gast Account" + +#: param/loadparm.c:697 +msgid "invalid users" +msgstr "Ungültige Benutzer" + +#: param/loadparm.c:698 +msgid "valid users" +msgstr "Gültige Benutzer" + +#: param/loadparm.c:699 +msgid "admin users" +msgstr "Administratoren" + +#: param/loadparm.c:700 +msgid "read list" +msgstr "" + +#: param/loadparm.c:701 +msgid "write list" +msgstr "" + +#: param/loadparm.c:702 +msgid "printer admin" +msgstr "Druckerverwalter" + +#: param/loadparm.c:703 +msgid "force user" +msgstr "" + +#: param/loadparm.c:704 +msgid "force group" +msgstr "" + +#: param/loadparm.c:705 +msgid "group" +msgstr "Gruppe" + +#: param/loadparm.c:707 +msgid "read only" +msgstr "nur lesen" + +#: param/loadparm.c:708 +msgid "write ok" +msgstr "Schreiben zulassen" + +#: param/loadparm.c:709 +msgid "writeable" +msgstr "Beschreibbar" + +#: param/loadparm.c:710 +msgid "writable" +msgstr "Beschreibbar" + +#: param/loadparm.c:712 +msgid "create mask" +msgstr "Erstellungsmaske" + +#: param/loadparm.c:713 +msgid "create mode" +msgstr "Erstellungsmodus" + +#: param/loadparm.c:714 +msgid "force create mode" +msgstr "Erzwinge Erstellungsmodus" + +#: param/loadparm.c:715 +msgid "security mask" +msgstr "" + +#: param/loadparm.c:716 +msgid "force security mode" +msgstr "Erzwinge Sicherheitsmodus" + +#: param/loadparm.c:717 +msgid "directory mask" +msgstr "Verzeichnismaske" + +#: param/loadparm.c:718 +msgid "directory mode" +msgstr "Verzeichnismodus" + +#: param/loadparm.c:719 +msgid "force directory mode" +msgstr "Erzwinge Verzeichnismodus" + +#: param/loadparm.c:720 +msgid "directory security mask" +msgstr "Verzeichnis Sicherheitsmaske" + +#: param/loadparm.c:721 +msgid "force directory security mode" +msgstr "Erzwinge Verzeichnis Sicherheitsmodus" + +#: param/loadparm.c:722 +msgid "inherit permissions" +msgstr "Vererbe Rechte" + +#: param/loadparm.c:723 +msgid "guest only" +msgstr "nur Gäste" + +#: param/loadparm.c:724 +msgid "only guest" +msgstr "nur Gäste" + +#: param/loadparm.c:726 +msgid "guest ok" +msgstr "Gäste erlaubt" + +#: param/loadparm.c:727 +msgid "public" +msgstr "Öffentlich" + +#: param/loadparm.c:729 +msgid "only user" +msgstr "Nur Benutzer" + +#: param/loadparm.c:730 +msgid "hosts allow" +msgstr "Erlaube hosts" + +#: param/loadparm.c:731 +msgid "allow hosts" +msgstr "Erlaube hosts" + +#: param/loadparm.c:732 +msgid "hosts deny" +msgstr "verbiete hosts" + +#: param/loadparm.c:733 +msgid "deny hosts" +msgstr "verbiete hosts" + +#: param/loadparm.c:736 +msgid "Secure Socket Layer Options" +msgstr "Secure Socket Layer Optionen" + +#: param/loadparm.c:737 +msgid "ssl" +msgstr "" + +#: param/loadparm.c:739 +msgid "ssl hosts" +msgstr "" + +#: param/loadparm.c:740 +msgid "ssl hosts resign" +msgstr "" + +#: param/loadparm.c:741 +msgid "ssl CA certDir" +msgstr "" + +#: param/loadparm.c:742 +msgid "ssl CA certFile" +msgstr "" + +#: param/loadparm.c:743 +msgid "ssl server cert" +msgstr "" + +#: param/loadparm.c:744 +msgid "ssl server key" +msgstr "" + +#: param/loadparm.c:745 +msgid "ssl client cert" +msgstr "" + +#: param/loadparm.c:746 +msgid "ssl client key" +msgstr "" + +#: param/loadparm.c:747 +msgid "ssl require clientcert" +msgstr "ssl bedarf eines Clientzertifikats" + +#: param/loadparm.c:748 +msgid "ssl require servercert" +msgstr "ssl bedarf eines Serverzertifikats" + +#: param/loadparm.c:749 +msgid "ssl ciphers" +msgstr "" + +#: param/loadparm.c:750 +msgid "ssl version" +msgstr "" + +#: param/loadparm.c:751 +msgid "ssl compatibility" +msgstr "" + +#: param/loadparm.c:754 +msgid "Logging Options" +msgstr "Log Optionen" + +#: param/loadparm.c:755 +msgid "log level" +msgstr "Log Stufe" + +#: param/loadparm.c:756 +msgid "debuglevel" +msgstr "Debug Stufe" + +#: param/loadparm.c:757 +msgid "syslog" +msgstr "" + +#: param/loadparm.c:758 +msgid "syslog only" +msgstr "nur syslog" + +#: param/loadparm.c:759 +msgid "log file" +msgstr "Log Datei" + +#: param/loadparm.c:761 +msgid "max log size" +msgstr "max log Grösse" + +#: param/loadparm.c:762 +msgid "timestamp logs" +msgstr "" + +#: param/loadparm.c:763 +msgid "debug timestamp" +msgstr "" + +#: param/loadparm.c:764 +msgid "debug hires timestamp" +msgstr "" + +#: param/loadparm.c:765 +msgid "debug pid" +msgstr "" + +#: param/loadparm.c:766 +msgid "debug uid" +msgstr "" + +#: param/loadparm.c:768 +msgid "Protocol Options" +msgstr " Protokoll Optionen" + +#: param/loadparm.c:770 +msgid "protocol" +msgstr "Protokoll" + +#: param/loadparm.c:771 +msgid "large readwrite" +msgstr "" + +#: param/loadparm.c:772 +msgid "max protocol" +msgstr "max Protokoll" + +#: param/loadparm.c:773 +msgid "min protocol" +msgstr "min Protokoll" + +#: param/loadparm.c:774 +msgid "unicode" +msgstr "" + +#: param/loadparm.c:775 +msgid "read bmpx" +msgstr "" + +#: param/loadparm.c:776 +msgid "read raw" +msgstr "" + +#: param/loadparm.c:777 +msgid "write raw" +msgstr "" + +#: param/loadparm.c:779 +msgid "nt smb support" +msgstr "nt smb Unterstützung" + +#: param/loadparm.c:780 +msgid "nt pipe support" +msgstr "nt pipe Unterstützung" + +#: param/loadparm.c:781 +msgid "nt acl support" +msgstr "ntacl Unterstützung" + +#: param/loadparm.c:782 +msgid "announce version" +msgstr "Melde Version" + +#: param/loadparm.c:783 +msgid "announce as" +msgstr "Melde als" + +#: param/loadparm.c:784 +msgid "max mux" +msgstr "" + +#: param/loadparm.c:785 +msgid "max xmit" +msgstr "" + +#: param/loadparm.c:787 +msgid "name resolve order" +msgstr "Reihenfolge Namensauflösung" + +#: param/loadparm.c:788 +msgid "max packet" +msgstr "max Paket" + +#: param/loadparm.c:789 +msgid "packet size" +msgstr "Paketgröße" + +#: param/loadparm.c:790 +msgid "max ttl" +msgstr "" + +#: param/loadparm.c:791 +msgid "max wins ttl" +msgstr "" + +#: param/loadparm.c:792 +msgid "min wins ttl" +msgstr "wins ttl minimo" + +#: param/loadparm.c:793 +msgid "time server" +msgstr "Zeitserver" + +#: param/loadparm.c:795 +msgid "Tuning Options" +msgstr "Optimierungsoptionen" + +#: param/loadparm.c:797 +msgid "change notify timeout" +msgstr "" + +#: param/loadparm.c:798 +msgid "deadtime" +msgstr "" + +#: param/loadparm.c:799 +msgid "getwd cache" +msgstr "" + +#: param/loadparm.c:800 +msgid "keepalive" +msgstr "" + +#: param/loadparm.c:802 +msgid "lpq cache time" +msgstr "" + +#: param/loadparm.c:803 +msgid "max smbd processes" +msgstr "Max Anzahl smbd Prozesse" + +#: param/loadparm.c:804 +msgid "max connections" +msgstr "Max. Verbindungen" + +#: param/loadparm.c:805 +msgid "paranoid server security" +msgstr "Paranoide Serversicherheit" + +#: param/loadparm.c:806 +msgid "max disk size" +msgstr "Max. Festplattengröße" + +#: param/loadparm.c:807 +msgid "max open files" +msgstr "max Anzahl offener Dateien" + +#: param/loadparm.c:808 +msgid "min print space" +msgstr "" + +#: param/loadparm.c:809 +msgid "read size" +msgstr "" + +#: param/loadparm.c:811 +msgid "socket options" +msgstr "Socket Optionen" + +#: param/loadparm.c:812 +msgid "stat cache size" +msgstr "Grösse stat cache" + +#: param/loadparm.c:813 +msgid "strict allocate" +msgstr "" + +#: param/loadparm.c:814 +msgid "strict sync" +msgstr "" + +#: param/loadparm.c:815 +msgid "sync always" +msgstr "" + +#: param/loadparm.c:816 +msgid "use mmap" +msgstr "verwende mmap" + +#: param/loadparm.c:817 +msgid "hostname lookups" +msgstr "" + +#: param/loadparm.c:818 +msgid "write cache size" +msgstr "größe Schreibpuffer" + +#: param/loadparm.c:820 +msgid "Printing Options" +msgstr "Druckoptionen" + +#: param/loadparm.c:822 +msgid "total print jobs" +msgstr "Druckaufträge insges." + +#: param/loadparm.c:823 +msgid "max print jobs" +msgstr "Druckaufträge max." + +#: param/loadparm.c:824 +msgid "load printers" +msgstr "lade Drucker" + +#: param/loadparm.c:825 +msgid "printcap name" +msgstr "printcap name" + +#: param/loadparm.c:826 +msgid "printcap" +msgstr "" + +#: param/loadparm.c:827 +msgid "printable" +msgstr "Bedruckbar" + +#: param/loadparm.c:828 +msgid "print ok" +msgstr "Druck ok" + +#: param/loadparm.c:829 +msgid "postscript" +msgstr "" + +#: param/loadparm.c:830 +msgid "printing" +msgstr "Druck" + +#: param/loadparm.c:831 +msgid "print command" +msgstr "Druckbefehl" + +#: param/loadparm.c:832 +msgid "disable spoolss" +msgstr "deaktiviere spoolss" + +#: param/loadparm.c:833 +msgid "lpq command" +msgstr "lpq Befehl" + +#: param/loadparm.c:834 +msgid "lprm command" +msgstr "lprm Befehl" + +#: param/loadparm.c:835 +msgid "lppause command" +msgstr "lppause Befehl" + +#: param/loadparm.c:836 +msgid "lpresume command" +msgstr "lpresume Befehl" + +#: param/loadparm.c:837 +msgid "queuepause command" +msgstr "queuepause Befehl" + +#: param/loadparm.c:838 +msgid "queueresume command" +msgstr "queueresume Befehl" + +#: param/loadparm.c:840 +msgid "enumports command" +msgstr "enumports Befehl" + +#: param/loadparm.c:841 +msgid "addprinter command" +msgstr "addprinter Befehl" + +#: param/loadparm.c:842 +msgid "deleteprinter command" +msgstr "deleteprinter Befehl" + +#: param/loadparm.c:843 +msgid "show add printer wizard" +msgstr "Zeige Wizzard zum hinzufügen von Druckern" + +#: param/loadparm.c:844 +msgid "os2 driver map" +msgstr "" + +#: param/loadparm.c:846 +msgid "printer name" +msgstr "Druckername" + +#: param/loadparm.c:847 +msgid "printer" +msgstr "Drucker + +#: param/loadparm.c:848 +msgid "use client driver" +msgstr "Verwende client Treiber" + +#: param/loadparm.c:849 +msgid "printer driver" +msgstr "Druckertreiber" + +#: param/loadparm.c:850 +msgid "printer driver file" +msgstr "Druckertreiber Datei" + +#: param/loadparm.c:851 +msgid "printer driver location" +msgstr "Pfad Druckertreiber" + +#: param/loadparm.c:853 +msgid "Filename Handling" +msgstr "Verwaltung Dateinamen" + +#: param/loadparm.c:854 +msgid "strip dot" +msgstr "Entferne den Punkt" + +#: param/loadparm.c:856 +msgid "mangled stack" +msgstr "" + +#: param/loadparm.c:857 +msgid "default case" +msgstr "" + +#: param/loadparm.c:858 +msgid "case sensitive" +msgstr "" + +#: param/loadparm.c:859 +msgid "casesignames" +msgstr "" + +#: param/loadparm.c:860 +msgid "preserve case" +msgstr "" + +#: param/loadparm.c:861 +msgid "short preserve case" +msgstr "" + +#: param/loadparm.c:862 +msgid "mangle case" +msgstr "" + +#: param/loadparm.c:863 +msgid "mangling char" +msgstr "" + +#: param/loadparm.c:864 +msgid "hide dot files" +msgstr "" + +#: param/loadparm.c:865 +msgid "hide unreadable" +msgstr "" + +#: param/loadparm.c:866 +msgid "delete veto files" +msgstr "" + +#: param/loadparm.c:867 +msgid "veto files" +msgstr "" + +#: param/loadparm.c:868 +msgid "hide files" +msgstr "Verstecke Dateien" + +#: param/loadparm.c:869 +msgid "veto oplock files" +msgstr "" + +#: param/loadparm.c:870 +msgid "map system" +msgstr "" + +#: param/loadparm.c:871 +msgid "map hidden" +msgstr "" + +#: param/loadparm.c:872 +msgid "map archive" +msgstr "" + +#: param/loadparm.c:873 +msgid "mangled names" +msgstr "" + +#: param/loadparm.c:874 +msgid "mangled map" +msgstr "" + +#: param/loadparm.c:875 +msgid "stat cache" +msgstr "" + +#: param/loadparm.c:877 +msgid "Domain Options" +msgstr "Domänen Optionen" + +#: param/loadparm.c:879 +msgid "domain admin group" +msgstr "Gruppe Domänenadministratoren" + +#: param/loadparm.c:880 +msgid "domain guest group" +msgstr "Domänen Gastgruppen" + +#: param/loadparm.c:883 +msgid "groupname map" +msgstr "" + +#: param/loadparm.c:886 +msgid "machine password timeout" +msgstr "Verfall Maschinenpasswort" + +#: param/loadparm.c:888 +msgid "Logon Options" +msgstr "Login optionen" + +#: param/loadparm.c:890 +msgid "add user script" +msgstr "" + +#: param/loadparm.c:891 +msgid "delete user script" +msgstr "" + +#: param/loadparm.c:892 +msgid "add group script" +msgstr "" + +#: param/loadparm.c:893 +msgid "delete group script" +msgstr "" + +#: param/loadparm.c:894 +msgid "add user to group script" +msgstr "" + +#: param/loadparm.c:895 +msgid "delete user from group script" +msgstr "" + +#: param/loadparm.c:896 +msgid "add machine script" +msgstr "" + +#: param/loadparm.c:897 +msgid "shutdown script" +msgstr "" + +#: param/loadparm.c:898 +msgid "abort shutdown script" +msgstr "" + +#: param/loadparm.c:900 +msgid "logon script" +msgstr "" + +#: param/loadparm.c:901 +msgid "logon path" +msgstr "" + +#: param/loadparm.c:902 +msgid "logon drive" +msgstr "" + +#: param/loadparm.c:903 +msgid "logon home" +msgstr "" + +#: param/loadparm.c:904 +msgid "domain logons" +msgstr "" + +#: param/loadparm.c:906 +msgid "Browse Options" +msgstr "Browsing Optionen" + +#: param/loadparm.c:908 +msgid "os level" +msgstr "os Stufe" + +#: param/loadparm.c:909 +msgid "lm announce" +msgstr "" + +#: param/loadparm.c:910 +msgid "lm interval" +msgstr "" + +#: param/loadparm.c:911 +msgid "preferred master" +msgstr "Bevorzugter master" + +#: param/loadparm.c:912 +msgid "prefered master" +msgstr "Bevorzugter master" + +#: param/loadparm.c:913 +msgid "local master" +msgstr "Lokaler master" + +#: param/loadparm.c:914 +msgid "domain master" +msgstr "Domänen master" + +#: param/loadparm.c:915 +msgid "browse list" +msgstr "browsing Liste" + +#: param/loadparm.c:916 +msgid "browseable" +msgstr "" + +#: param/loadparm.c:917 +msgid "browsable" +msgstr "" + +#: param/loadparm.c:918 +msgid "enhanced browsing" +msgstr "Erweitertes browsing" + +#: param/loadparm.c:920 +msgid "WINS Options" +msgstr "WINS Optionen" + +#: param/loadparm.c:921 +msgid "dns proxy" +msgstr "" + +#: param/loadparm.c:922 +msgid "wins proxy" +msgstr "" + +#: param/loadparm.c:924 +msgid "wins server" +msgstr "" + +#: param/loadparm.c:925 +msgid "wins support" +msgstr "" + +#: param/loadparm.c:926 +msgid "wins hook" +msgstr "" + +#: param/loadparm.c:928 +msgid "Locking Options" +msgstr "Locking Optionen" + +#: param/loadparm.c:930 +msgid "blocking locks" +msgstr "" + +#: param/loadparm.c:931 +msgid "fake oplocks" +msgstr "" + +#: param/loadparm.c:932 +msgid "kernel oplocks" +msgstr "" + +#: param/loadparm.c:933 +msgid "locking" +msgstr "" + +#: param/loadparm.c:935 +msgid "oplocks" +msgstr "" + +#: param/loadparm.c:936 +msgid "level2 oplocks" +msgstr "" + +#: param/loadparm.c:937 +msgid "oplock break wait time" +msgstr "" + +#: param/loadparm.c:938 +msgid "oplock contention limit" +msgstr "" + +#: param/loadparm.c:939 +msgid "posix locking" +msgstr "" + +#: param/loadparm.c:940 +msgid "strict locking" +msgstr "" + +#: param/loadparm.c:941 +msgid "share modes" +msgstr "" + +#: param/loadparm.c:944 +msgid "Ldap Options" +msgstr "LDAP Optionen" + +#: param/loadparm.c:946 +msgid "ldap server" +msgstr "" + +#: param/loadparm.c:947 +msgid "ldap port" +msgstr "" + +#: param/loadparm.c:948 +msgid "ldap suffix" +msgstr "" + +#: param/loadparm.c:949 +msgid "ldap filter" +msgstr "" + +#: param/loadparm.c:950 +msgid "ldap root" +msgstr "" + +#: param/loadparm.c:951 +msgid "ldap root passwd" +msgstr "" + +#: param/loadparm.c:954 +msgid "Miscellaneous Options" +msgstr "Verschiedene Optionen" + +#: param/loadparm.c:955 +msgid "add share command" +msgstr "" + +#: param/loadparm.c:956 +msgid "change share command" +msgstr "" + +#: param/loadparm.c:957 +msgid "delete share command" +msgstr "" + +#: param/loadparm.c:959 +msgid "config file" +msgstr "Konfigurationsdatei" + +#: param/loadparm.c:960 +msgid "preload" +msgstr "Lade im Voraus" + +#: param/loadparm.c:961 +msgid "auto services" +msgstr "" + +#: param/loadparm.c:962 +msgid "lock dir" +msgstr "Lock Verzeichnis" + +#: param/loadparm.c:963 +msgid "lock directory" +msgstr "Lock Verzeichnis" + +#: param/loadparm.c:965 +msgid "utmp directory" +msgstr "utmp Verzeichnis" + +#: param/loadparm.c:966 +msgid "wtmp directory" +msgstr "wtmp Verzeichnis" + +#: param/loadparm.c:967 +msgid "utmp" +msgstr "" + +#: param/loadparm.c:970 +msgid "default service" +msgstr "" + +#: param/loadparm.c:971 +msgid "default" +msgstr "" + +#: param/loadparm.c:972 +msgid "message command" +msgstr "Message Befehl" + +#: param/loadparm.c:973 +msgid "dfree command" +msgstr "dfree Befehl" + +#: param/loadparm.c:974 +msgid "remote announce" +msgstr "remote announce" + +#: param/loadparm.c:975 +msgid "remote browse sync" +msgstr "" + +#: param/loadparm.c:976 +msgid "socket address" +msgstr "" + +#: param/loadparm.c:977 +msgid "homedir map" +msgstr "" + +#: param/loadparm.c:978 +msgid "time offset" +msgstr "" + +#: param/loadparm.c:979 +msgid "NIS homedir" +msgstr "" + +#: param/loadparm.c:980 +msgid "-valid" +msgstr "" + +#: param/loadparm.c:982 +msgid "copy" +msgstr "Kopie" + +#: param/loadparm.c:983 +msgid "include" +msgstr "include" + +#: param/loadparm.c:984 +msgid "exec" +msgstr "" + +#: param/loadparm.c:985 +msgid "preexec" +msgstr "" + +#: param/loadparm.c:987 +msgid "preexec close" +msgstr "" + +#: param/loadparm.c:988 +msgid "postexec" +msgstr "" + +#: param/loadparm.c:989 +msgid "root preexec" +msgstr "" + +#: param/loadparm.c:990 +msgid "root preexec close" +msgstr "" + +#: param/loadparm.c:991 +msgid "root postexec" +msgstr "" + +#: param/loadparm.c:992 +msgid "available" +msgstr "Verfügbar" + +#: param/loadparm.c:993 +msgid "volume" +msgstr "" + +#: param/loadparm.c:994 +msgid "fstype" +msgstr "Typ Dateisystem" + +#: param/loadparm.c:995 +msgid "set directory" +msgstr "" + +#: param/loadparm.c:996 +msgid "source environment" +msgstr "" + +#: param/loadparm.c:997 +msgid "wide links" +msgstr "" + +#: param/loadparm.c:998 +msgid "follow symlinks" +msgstr "Folge symlinks" + +#: param/loadparm.c:999 +msgid "dont descend" +msgstr "nicht hinabsteigen" + +#: param/loadparm.c:1000 +msgid "magic script" +msgstr "" + +#: param/loadparm.c:1001 +msgid "magic output" +msgstr "" + +#: param/loadparm.c:1002 +msgid "delete readonly" +msgstr "Lösche nur-lesen" + +#: param/loadparm.c:1003 +msgid "dos filemode" +msgstr "" + +#: param/loadparm.c:1004 +msgid "dos filetimes" +msgstr "" + +#: param/loadparm.c:1005 +msgid "dos filetime resolution" +msgstr "" + +#: param/loadparm.c:1007 +msgid "fake directory create times" +msgstr "" + +#: param/loadparm.c:1008 +msgid "panic action" +msgstr "" + +#: param/loadparm.c:1009 +msgid "hide local users" +msgstr "verstecke lokale Benutzer" + +#: param/loadparm.c:1012 +msgid "VFS options" +msgstr "VFS Optionen" + +#: param/loadparm.c:1014 +msgid "vfs object" +msgstr "" + +#: param/loadparm.c:1015 +msgid "vfs options" +msgstr "" + +#: param/loadparm.c:1018 +msgid "msdfs root" +msgstr "" + +#: param/loadparm.c:1019 +msgid "host msdfs" +msgstr "" + +#: param/loadparm.c:1021 +msgid "Winbind options" +msgstr "Winbind Optionen" + +#: param/loadparm.c:1023 +msgid "winbind uid" +msgstr "" + +#: param/loadparm.c:1024 +msgid "winbind gid" +msgstr "" + +#: param/loadparm.c:1025 +msgid "template homedir" +msgstr "" + +#: param/loadparm.c:1026 +msgid "template shell" +msgstr "" + +#: param/loadparm.c:1027 +msgid "winbind separator" +msgstr "" + +#: param/loadparm.c:1028 +msgid "winbind cache time" +msgstr "" + +#: param/loadparm.c:1029 +msgid "winbind enum users" +msgstr "" + +#: param/loadparm.c:1030 +msgid "winbind enum groups" +msgstr "" diff --git a/source4/po/en.msg b/source4/po/en.msg new file mode 100644 index 0000000000..2f78cb835e --- /dev/null +++ b/source4/po/en.msg @@ -0,0 +1,1707 @@ +# English messages for international release of SWAT. +# Copyright (C) 2001 Free Software Foundation, Inc. +# TAKAHASHI Motonobu , 2001. +msgid "" +msgstr "" +"Project-Id-Version: i18n_swat \n" +"POT-Creation-Date: 2001-09-20 20:29+0900\n" +"PO-Revision-Date: 2000-02-08 12:48+09:00\n" +"Last-Translator: TAKAHASHI Motonobu \n" +"Language-Team: (Samba Team) \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=US-ASCII\n" +"Content-Transfer-Encoding: \n" + +#: web/swat.c:120 +#, c-format +msgid "ERROR: Can't open %s\n" +msgstr "" + +#. +#. str = stripspace(parm->label); +#. strlower (str); //monyo +#. d_printf("%s       %s", +#. str, _("Help"), parm->label); +#. +#: web/swat.c:211 +msgid "Help" +msgstr "" + +#: web/swat.c:217 web/swat.c:231 web/swat.c:246 web/swat.c:254 web/swat.c:263 +#: web/swat.c:272 web/swat.c:278 web/swat.c:284 web/swat.c:297 +msgid "Set Default" +msgstr "" + +#: web/swat.c:502 +#, c-format +msgid "Logged in as %s

\n" +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:512 +msgid "Status" +msgstr "" + +#: web/swat.c:513 +msgid "View Config" +msgstr "" + +#: web/swat.c:515 +msgid "Password Management" +msgstr "" + +#: web/swat.c:539 +msgid "Current Config" +msgstr "" + +#: web/swat.c:543 +msgid "Normal View" +msgstr "" + +#: web/swat.c:545 +msgid "Full View" +msgstr "" + +#: web/swat.c:561 +msgid "Global Variables" +msgstr "" + +#: web/swat.c:575 web/swat.c:671 web/swat.c:1014 +msgid "Commit Changes" +msgstr "" + +#: web/swat.c:579 web/swat.c:674 web/swat.c:1016 +msgid "Reset Values" +msgstr "" + +#: web/swat.c:581 web/swat.c:676 web/swat.c:1018 +msgid "Advanced View" +msgstr "" + +#: web/swat.c:583 web/swat.c:678 web/swat.c:1020 +msgid "Basic View" +msgstr "" + +#: web/swat.c:613 +msgid "Share Parameters" +msgstr "" + +#: web/swat.c:642 +msgid "Choose Share" +msgstr "" + +#: web/swat.c:656 +msgid "Delete Share" +msgstr "" + +#: web/swat.c:663 +msgid "Create Share" +msgstr "" + +#: web/swat.c:708 +msgid "password change in demo mode rejected\n" +msgstr "" + +#: web/swat.c:747 +msgid " Must specify \"User Name\" \n" +msgstr "" + +#: web/swat.c:763 +msgid " Must specify \"Old Password\" \n" +msgstr "" + +#: web/swat.c:769 +msgid " Must specify \"Remote Machine\" \n" +msgstr "" + +#: web/swat.c:776 +msgid " Must specify \"New, and Re-typed Passwords\" \n" +msgstr "" + +#: web/swat.c:782 +msgid " Re-typed password didn't match new password\n" +msgstr "" + +#: web/swat.c:812 +#, c-format +msgid " The passwd for '%s' has been changed. \n" +msgstr "" + +#: web/swat.c:814 +#, c-format +msgid " The passwd for '%s' has NOT been changed. \n" +msgstr "" + +#: web/swat.c:838 +msgid "Server Password Management" +msgstr "" + +#. +#. * Create all the dialog boxes for data collection +#. +#: web/swat.c:847 web/swat.c:894 +msgid " User Name : " +msgstr "" + +#: web/swat.c:850 web/swat.c:896 +msgid " Old Password : " +msgstr "" + +#: web/swat.c:853 web/swat.c:898 +msgid " New Password : " +msgstr "" + +#: web/swat.c:855 web/swat.c:900 +msgid " Re-type New Password : " +msgstr "" + +#: web/swat.c:863 web/swat.c:911 +msgid "Change Password" +msgstr "" + +#: web/swat.c:866 +msgid "Add New User" +msgstr "" + +#: web/swat.c:868 +msgid "Delete User" +msgstr "" + +#: web/swat.c:870 +msgid "Disable User" +msgstr "" + +#: web/swat.c:872 +msgid "Enable User" +msgstr "" + +#: web/swat.c:885 +msgid "Client/Server Password Management" +msgstr "" + +#: web/swat.c:902 +msgid " Remote Machine : " +msgstr "" + +#: web/swat.c:940 +msgid "Printer Parameters" +msgstr "" + +#: web/swat.c:942 +msgid "Important Note:" +msgstr "" + +#: web/swat.c:943 +msgid "Printer names marked with [*] in the Choose Printer drop-down box " +msgstr "" + +#: web/swat.c:944 +msgid "are autoloaded printers from " +msgstr "" + +#: web/swat.c:945 +msgid "Printcap Name" +msgstr "" + +#: web/swat.c:946 +msgid "Attempting to delete these printers from SWAT will have no effect.\n" +msgstr "" + +#: web/swat.c:980 +msgid "Choose Printer" +msgstr "" + +#: web/swat.c:999 +msgid "Delete Printer" +msgstr "" + +#: web/swat.c:1006 +msgid "Create Printer" +msgstr "" + +#: web/statuspage.c:40 +msgid "DENY_NONE" +msgstr "" + +#: web/statuspage.c:41 +msgid "DENY_ALL " +msgstr "" + +#: web/statuspage.c:42 +msgid "DENY_DOS " +msgstr "" + +#: web/statuspage.c:43 +msgid "DENY_READ " +msgstr "" + +#: web/statuspage.c:44 +msgid "DENY_WRITE " +msgstr "" + +#: web/statuspage.c:50 +msgid "RDONLY " +msgstr "" + +#: web/statuspage.c:51 +msgid "WRONLY " +msgstr "" + +#: web/statuspage.c:52 +msgid "RDWR " +msgstr "" + +#: web/statuspage.c:60 +msgid "EXCLUSIVE+BATCH " +msgstr "" + +#: web/statuspage.c:62 +msgid "EXCLUSIVE " +msgstr "" + +#: web/statuspage.c:64 +msgid "BATCH " +msgstr "" + +#: web/statuspage.c:66 +msgid "LEVEL_II " +msgstr "" + +#: web/statuspage.c:68 +msgid "NONE " +msgstr "" + +#: web/statuspage.c:195 +msgid "Server Status" +msgstr "" + +#: web/statuspage.c:200 +msgid "Auto Refresh" +msgstr "" + +#: web/statuspage.c:201 web/statuspage.c:206 +msgid "Refresh Interval: " +msgstr "" + +#: web/statuspage.c:205 +msgid "Stop Refreshing" +msgstr "" + +#: web/statuspage.c:220 +msgid "version:" +msgstr "" + +#: web/statuspage.c:223 +msgid "smbd:" +msgstr "" + +#: web/statuspage.c:223 web/statuspage.c:235 +msgid "running" +msgstr "" + +#: web/statuspage.c:223 web/statuspage.c:235 +msgid "not running" +msgstr "" + +#: web/statuspage.c:226 +msgid "Stop smbd" +msgstr "" + +#: web/statuspage.c:228 +msgid "Start smbd" +msgstr "" + +#: web/statuspage.c:230 +msgid "Restart smbd" +msgstr "" + +#: web/statuspage.c:235 +msgid "nmbd:" +msgstr "" + +#: web/statuspage.c:238 +msgid "Stop nmbd" +msgstr "" + +#: web/statuspage.c:240 +msgid "Start nmbd" +msgstr "" + +#: web/statuspage.c:242 +msgid "Restart nmbd" +msgstr "" + +#: web/statuspage.c:249 +msgid "Active Connections" +msgstr "" + +#: web/statuspage.c:251 web/statuspage.c:264 web/statuspage.c:272 +msgid "PID" +msgstr "" + +#: web/statuspage.c:251 web/statuspage.c:264 +msgid "Client" +msgstr "" + +#: web/statuspage.c:251 +msgid "IP address" +msgstr "" + +#: web/statuspage.c:251 web/statuspage.c:264 web/statuspage.c:272 +msgid "Date" +msgstr "" + +#: web/statuspage.c:253 +msgid "Kill" +msgstr "" + +#: web/statuspage.c:261 +msgid "Active Shares" +msgstr "" + +#: web/statuspage.c:264 +msgid "Share" +msgstr "" + +#: web/statuspage.c:264 +msgid "User" +msgstr "" + +#: web/statuspage.c:264 +msgid "Group" +msgstr "" + +#: web/statuspage.c:270 +msgid "Open Files" +msgstr "" + +#: web/statuspage.c:272 +msgid "Sharing" +msgstr "" + +#: web/statuspage.c:272 +msgid "R/W" +msgstr "" + +#: web/statuspage.c:272 +msgid "Oplock" +msgstr "" + +#: web/statuspage.c:272 +msgid "File" +msgstr "" + +#: param/loadparm.c:641 +msgid "Base Options" +msgstr "" + +#: param/loadparm.c:643 +msgid "dos charset" +msgstr "" + +#: param/loadparm.c:644 +msgid "unix charset" +msgstr "" + +#: param/loadparm.c:645 +msgid "display charset" +msgstr "" + +#: param/loadparm.c:646 +msgid "comment" +msgstr "" + +#: param/loadparm.c:647 +msgid "path" +msgstr "" + +#: param/loadparm.c:648 +msgid "directory" +msgstr "" + +#: param/loadparm.c:649 +msgid "workgroup" +msgstr "" + +#: param/loadparm.c:650 +msgid "netbios name" +msgstr "" + +#: param/loadparm.c:651 +msgid "netbios aliases" +msgstr "" + +#: param/loadparm.c:652 +msgid "netbios scope" +msgstr "" + +#: param/loadparm.c:653 +msgid "server string" +msgstr "" + +#: param/loadparm.c:654 +msgid "interfaces" +msgstr "" + +#: param/loadparm.c:655 +msgid "bind interfaces only" +msgstr "" + +#: param/loadparm.c:657 +msgid "Security Options" +msgstr "" + +#: param/loadparm.c:659 +msgid "security" +msgstr "" + +#: param/loadparm.c:660 +msgid "encrypt passwords" +msgstr "" + +#: param/loadparm.c:661 +msgid "update encrypted" +msgstr "" + +#: param/loadparm.c:662 +msgid "allow trusted domains" +msgstr "" + +#: param/loadparm.c:663 +msgid "alternate permissions" +msgstr "" + +#: param/loadparm.c:664 +msgid "hosts equiv" +msgstr "" + +#: param/loadparm.c:665 +msgid "min passwd length" +msgstr "" + +#: param/loadparm.c:666 +msgid "min password length" +msgstr "" + +#: param/loadparm.c:667 +msgid "map to guest" +msgstr "" + +#: param/loadparm.c:668 +msgid "null passwords" +msgstr "" + +#: param/loadparm.c:669 +msgid "obey pam restrictions" +msgstr "" + +#: param/loadparm.c:670 +msgid "password server" +msgstr "" + +#: param/loadparm.c:671 +msgid "smb passwd file" +msgstr "" + +#: param/loadparm.c:672 +msgid "private dir" +msgstr "" + +#: param/loadparm.c:673 +msgid "passdb module path" +msgstr "" + +#: param/loadparm.c:674 +msgid "root directory" +msgstr "" + +#: param/loadparm.c:675 +msgid "root dir" +msgstr "" + +#: param/loadparm.c:676 +msgid "root" +msgstr "" + +#: param/loadparm.c:678 +msgid "pam password change" +msgstr "" + +#: param/loadparm.c:679 +msgid "passwd program" +msgstr "" + +#: param/loadparm.c:680 +msgid "passwd chat" +msgstr "" + +#: param/loadparm.c:681 +msgid "passwd chat debug" +msgstr "" + +#: param/loadparm.c:682 +msgid "username map" +msgstr "" + +#: param/loadparm.c:683 +msgid "password level" +msgstr "" + +#: param/loadparm.c:684 +msgid "username level" +msgstr "" + +#: param/loadparm.c:685 +msgid "unix password sync" +msgstr "" + +#: param/loadparm.c:686 +msgid "restrict anonymous" +msgstr "" + +#: param/loadparm.c:687 +msgid "lanman auth" +msgstr "" + +#: param/loadparm.c:688 +msgid "ntlm auth" +msgstr "" + +#: param/loadparm.c:689 +msgid "plaintext to smbpasswd" +msgstr "" + +#: param/loadparm.c:690 +msgid "use rhosts" +msgstr "" + +#: param/loadparm.c:692 +msgid "username" +msgstr "" + +#: param/loadparm.c:693 +msgid "user" +msgstr "" + +#: param/loadparm.c:694 +msgid "users" +msgstr "" + +#: param/loadparm.c:696 +msgid "guest account" +msgstr "" + +#: param/loadparm.c:697 +msgid "invalid users" +msgstr "" + +#: param/loadparm.c:698 +msgid "valid users" +msgstr "" + +#: param/loadparm.c:699 +msgid "admin users" +msgstr "" + +#: param/loadparm.c:700 +msgid "read list" +msgstr "" + +#: param/loadparm.c:701 +msgid "write list" +msgstr "" + +#: param/loadparm.c:702 +msgid "printer admin" +msgstr "" + +#: param/loadparm.c:703 +msgid "force user" +msgstr "" + +#: param/loadparm.c:704 +msgid "force group" +msgstr "" + +#: param/loadparm.c:705 +msgid "group" +msgstr "" + +#: param/loadparm.c:707 +msgid "read only" +msgstr "" + +#: param/loadparm.c:708 +msgid "write ok" +msgstr "" + +#: param/loadparm.c:709 +msgid "writeable" +msgstr "" + +#: param/loadparm.c:710 +msgid "writable" +msgstr "" + +#: param/loadparm.c:712 +msgid "create mask" +msgstr "" + +#: param/loadparm.c:713 +msgid "create mode" +msgstr "" + +#: param/loadparm.c:714 +msgid "force create mode" +msgstr "" + +#: param/loadparm.c:715 +msgid "security mask" +msgstr "" + +#: param/loadparm.c:716 +msgid "force security mode" +msgstr "" + +#: param/loadparm.c:717 +msgid "directory mask" +msgstr "" + +#: param/loadparm.c:718 +msgid "directory mode" +msgstr "" + +#: param/loadparm.c:719 +msgid "force directory mode" +msgstr "" + +#: param/loadparm.c:720 +msgid "directory security mask" +msgstr "" + +#: param/loadparm.c:721 +msgid "force directory security mode" +msgstr "" + +#: param/loadparm.c:722 +msgid "inherit permissions" +msgstr "" + +#: param/loadparm.c:723 +msgid "guest only" +msgstr "" + +#: param/loadparm.c:724 +msgid "only guest" +msgstr "" + +#: param/loadparm.c:726 +msgid "guest ok" +msgstr "" + +#: param/loadparm.c:727 +msgid "public" +msgstr "" + +#: param/loadparm.c:729 +msgid "only user" +msgstr "" + +#: param/loadparm.c:730 +msgid "hosts allow" +msgstr "" + +#: param/loadparm.c:731 +msgid "allow hosts" +msgstr "" + +#: param/loadparm.c:732 +msgid "hosts deny" +msgstr "" + +#: param/loadparm.c:733 +msgid "deny hosts" +msgstr "" + +#: param/loadparm.c:736 +msgid "Secure Socket Layer Options" +msgstr "" + +#: param/loadparm.c:737 +msgid "ssl" +msgstr "" + +#: param/loadparm.c:739 +msgid "ssl hosts" +msgstr "" + +#: param/loadparm.c:740 +msgid "ssl hosts resign" +msgstr "" + +#: param/loadparm.c:741 +msgid "ssl CA certDir" +msgstr "" + +#: param/loadparm.c:742 +msgid "ssl CA certFile" +msgstr "" + +#: param/loadparm.c:743 +msgid "ssl server cert" +msgstr "" + +#: param/loadparm.c:744 +msgid "ssl server key" +msgstr "" + +#: param/loadparm.c:745 +msgid "ssl client cert" +msgstr "" + +#: param/loadparm.c:746 +msgid "ssl client key" +msgstr "" + +#: param/loadparm.c:747 +msgid "ssl require clientcert" +msgstr "" + +#: param/loadparm.c:748 +msgid "ssl require servercert" +msgstr "" + +#: param/loadparm.c:749 +msgid "ssl ciphers" +msgstr "" + +#: param/loadparm.c:750 +msgid "ssl version" +msgstr "" + +#: param/loadparm.c:751 +msgid "ssl compatibility" +msgstr "" + +#: param/loadparm.c:754 +msgid "Logging Options" +msgstr "" + +#: param/loadparm.c:755 +msgid "log level" +msgstr "" + +#: param/loadparm.c:756 +msgid "debuglevel" +msgstr "" + +#: param/loadparm.c:757 +msgid "syslog" +msgstr "" + +#: param/loadparm.c:758 +msgid "syslog only" +msgstr "" + +#: param/loadparm.c:759 +msgid "log file" +msgstr "" + +#: param/loadparm.c:761 +msgid "max log size" +msgstr "" + +#: param/loadparm.c:762 +msgid "timestamp logs" +msgstr "" + +#: param/loadparm.c:763 +msgid "debug timestamp" +msgstr "" + +#: param/loadparm.c:764 +msgid "debug hires timestamp" +msgstr "" + +#: param/loadparm.c:765 +msgid "debug pid" +msgstr "" + +#: param/loadparm.c:766 +msgid "debug uid" +msgstr "" + +#: param/loadparm.c:768 +msgid "Protocol Options" +msgstr "" + +#: param/loadparm.c:770 +msgid "protocol" +msgstr "" + +#: param/loadparm.c:771 +msgid "large readwrite" +msgstr "" + +#: param/loadparm.c:772 +msgid "max protocol" +msgstr "" + +#: param/loadparm.c:773 +msgid "min protocol" +msgstr "" + +#: param/loadparm.c:774 +msgid "unicode" +msgstr "" + +#: param/loadparm.c:775 +msgid "read bmpx" +msgstr "" + +#: param/loadparm.c:776 +msgid "read raw" +msgstr "" + +#: param/loadparm.c:777 +msgid "write raw" +msgstr "" + +#: param/loadparm.c:779 +msgid "nt smb support" +msgstr "" + +#: param/loadparm.c:780 +msgid "nt pipe support" +msgstr "" + +#: param/loadparm.c:781 +msgid "nt acl support" +msgstr "" + +#: param/loadparm.c:782 +msgid "announce version" +msgstr "" + +#: param/loadparm.c:783 +msgid "announce as" +msgstr "" + +#: param/loadparm.c:784 +msgid "max mux" +msgstr "" + +#: param/loadparm.c:785 +msgid "max xmit" +msgstr "" + +#: param/loadparm.c:787 +msgid "name resolve order" +msgstr "" + +#: param/loadparm.c:788 +msgid "max packet" +msgstr "" + +#: param/loadparm.c:789 +msgid "packet size" +msgstr "" + +#: param/loadparm.c:790 +msgid "max ttl" +msgstr "" + +#: param/loadparm.c:791 +msgid "max wins ttl" +msgstr "" + +#: param/loadparm.c:792 +msgid "min wins ttl" +msgstr "" + +#: param/loadparm.c:793 +msgid "time server" +msgstr "" + +#: param/loadparm.c:795 +msgid "Tuning Options" +msgstr "" + +#: param/loadparm.c:797 +msgid "change notify timeout" +msgstr "" + +#: param/loadparm.c:798 +msgid "deadtime" +msgstr "" + +#: param/loadparm.c:799 +msgid "getwd cache" +msgstr "" + +#: param/loadparm.c:800 +msgid "keepalive" +msgstr "" + +#: param/loadparm.c:802 +msgid "lpq cache time" +msgstr "" + +#: param/loadparm.c:803 +msgid "max smbd processes" +msgstr "" + +#: param/loadparm.c:804 +msgid "max connections" +msgstr "" + +#: param/loadparm.c:805 +msgid "paranoid server security" +msgstr "" + +#: param/loadparm.c:806 +msgid "max disk size" +msgstr "" + +#: param/loadparm.c:807 +msgid "max open files" +msgstr "" + +#: param/loadparm.c:808 +msgid "min print space" +msgstr "" + +#: param/loadparm.c:809 +msgid "read size" +msgstr "" + +#: param/loadparm.c:811 +msgid "socket options" +msgstr "" + +#: param/loadparm.c:812 +msgid "stat cache size" +msgstr "" + +#: param/loadparm.c:813 +msgid "strict allocate" +msgstr "" + +#: param/loadparm.c:814 +msgid "strict sync" +msgstr "" + +#: param/loadparm.c:815 +msgid "sync always" +msgstr "" + +#: param/loadparm.c:816 +msgid "use mmap" +msgstr "" + +#: param/loadparm.c:817 +msgid "hostname lookups" +msgstr "" + +#: param/loadparm.c:818 +msgid "write cache size" +msgstr "" + +#: param/loadparm.c:820 +msgid "Printing Options" +msgstr "" + +#: param/loadparm.c:822 +msgid "total print jobs" +msgstr "" + +#: param/loadparm.c:823 +msgid "max print jobs" +msgstr "" + +#: param/loadparm.c:824 +msgid "load printers" +msgstr "" + +#: param/loadparm.c:825 +msgid "printcap name" +msgstr "" + +#: param/loadparm.c:826 +msgid "printcap" +msgstr "" + +#: param/loadparm.c:827 +msgid "printable" +msgstr "" + +#: param/loadparm.c:828 +msgid "print ok" +msgstr "" + +#: param/loadparm.c:829 +msgid "postscript" +msgstr "" + +#: param/loadparm.c:830 +msgid "printing" +msgstr "" + +#: param/loadparm.c:831 +msgid "print command" +msgstr "" + +#: param/loadparm.c:832 +msgid "disable spoolss" +msgstr "" + +#: param/loadparm.c:833 +msgid "lpq command" +msgstr "" + +#: param/loadparm.c:834 +msgid "lprm command" +msgstr "" + +#: param/loadparm.c:835 +msgid "lppause command" +msgstr "" + +#: param/loadparm.c:836 +msgid "lpresume command" +msgstr "" + +#: param/loadparm.c:837 +msgid "queuepause command" +msgstr "" + +#: param/loadparm.c:838 +msgid "queueresume command" +msgstr "" + +#: param/loadparm.c:840 +msgid "enumports command" +msgstr "" + +#: param/loadparm.c:841 +msgid "addprinter command" +msgstr "" + +#: param/loadparm.c:842 +msgid "deleteprinter command" +msgstr "" + +#: param/loadparm.c:843 +msgid "show add printer wizard" +msgstr "" + +#: param/loadparm.c:844 +msgid "os2 driver map" +msgstr "" + +#: param/loadparm.c:846 +msgid "printer name" +msgstr "" + +#: param/loadparm.c:847 +msgid "printer" +msgstr "" + +#: param/loadparm.c:848 +msgid "use client driver" +msgstr "" + +#: param/loadparm.c:849 +msgid "printer driver" +msgstr "" + +#: param/loadparm.c:850 +msgid "printer driver file" +msgstr "" + +#: param/loadparm.c:851 +msgid "printer driver location" +msgstr "" + +#: param/loadparm.c:853 +msgid "Filename Handling" +msgstr "" + +#: param/loadparm.c:854 +msgid "strip dot" +msgstr "" + +#: param/loadparm.c:856 +msgid "mangled stack" +msgstr "" + +#: param/loadparm.c:857 +msgid "default case" +msgstr "" + +#: param/loadparm.c:858 +msgid "case sensitive" +msgstr "" + +#: param/loadparm.c:859 +msgid "casesignames" +msgstr "" + +#: param/loadparm.c:860 +msgid "preserve case" +msgstr "" + +#: param/loadparm.c:861 +msgid "short preserve case" +msgstr "" + +#: param/loadparm.c:862 +msgid "mangle case" +msgstr "" + +#: param/loadparm.c:863 +msgid "mangling char" +msgstr "" + +#: param/loadparm.c:864 +msgid "hide dot files" +msgstr "" + +#: param/loadparm.c:865 +msgid "hide unreadable" +msgstr "" + +#: param/loadparm.c:866 +msgid "delete veto files" +msgstr "" + +#: param/loadparm.c:867 +msgid "veto files" +msgstr "" + +#: param/loadparm.c:868 +msgid "hide files" +msgstr "" + +#: param/loadparm.c:869 +msgid "veto oplock files" +msgstr "" + +#: param/loadparm.c:870 +msgid "map system" +msgstr "" + +#: param/loadparm.c:871 +msgid "map hidden" +msgstr "" + +#: param/loadparm.c:872 +msgid "map archive" +msgstr "" + +#: param/loadparm.c:873 +msgid "mangled names" +msgstr "" + +#: param/loadparm.c:874 +msgid "mangled map" +msgstr "" + +#: param/loadparm.c:875 +msgid "stat cache" +msgstr "" + +#: param/loadparm.c:877 +msgid "Domain Options" +msgstr "" + +#: param/loadparm.c:879 +msgid "domain admin group" +msgstr "" + +#: param/loadparm.c:880 +msgid "domain guest group" +msgstr "" + +#: param/loadparm.c:883 +msgid "groupname map" +msgstr "" + +#: param/loadparm.c:886 +msgid "machine password timeout" +msgstr "" + +#: param/loadparm.c:888 +msgid "Logon Options" +msgstr "" + +#: param/loadparm.c:890 +msgid "add user script" +msgstr "" + +#: param/loadparm.c:891 +msgid "delete user script" +msgstr "" + +#: param/loadparm.c:892 +msgid "add group script" +msgstr "" + +#: param/loadparm.c:893 +msgid "delete group script" +msgstr "" + +#: param/loadparm.c:894 +msgid "add user to group script" +msgstr "" + +#: param/loadparm.c:895 +msgid "delete user from group script" +msgstr "" + +#: param/loadparm.c:896 +msgid "add machine script" +msgstr "" + +#: param/loadparm.c:897 +msgid "shutdown script" +msgstr "" + +#: param/loadparm.c:898 +msgid "abort shutdown script" +msgstr "" + +#: param/loadparm.c:900 +msgid "logon script" +msgstr "" + +#: param/loadparm.c:901 +msgid "logon path" +msgstr "" + +#: param/loadparm.c:902 +msgid "logon drive" +msgstr "" + +#: param/loadparm.c:903 +msgid "logon home" +msgstr "" + +#: param/loadparm.c:904 +msgid "domain logons" +msgstr "" + +#: param/loadparm.c:906 +msgid "Browse Options" +msgstr "" + +#: param/loadparm.c:908 +msgid "os level" +msgstr "" + +#: param/loadparm.c:909 +msgid "lm announce" +msgstr "" + +#: param/loadparm.c:910 +msgid "lm interval" +msgstr "" + +#: param/loadparm.c:911 +msgid "preferred master" +msgstr "" + +#: param/loadparm.c:912 +msgid "prefered master" +msgstr "" + +#: param/loadparm.c:913 +msgid "local master" +msgstr "" + +#: param/loadparm.c:914 +msgid "domain master" +msgstr "" + +#: param/loadparm.c:915 +msgid "browse list" +msgstr "" + +#: param/loadparm.c:916 +msgid "browseable" +msgstr "" + +#: param/loadparm.c:917 +msgid "browsable" +msgstr "" + +#: param/loadparm.c:918 +msgid "enhanced browsing" +msgstr "" + +#: param/loadparm.c:920 +msgid "WINS Options" +msgstr "" + +#: param/loadparm.c:921 +msgid "dns proxy" +msgstr "" + +#: param/loadparm.c:922 +msgid "wins proxy" +msgstr "" + +#: param/loadparm.c:924 +msgid "wins server" +msgstr "" + +#: param/loadparm.c:925 +msgid "wins support" +msgstr "" + +#: param/loadparm.c:926 +msgid "wins hook" +msgstr "" + +#: param/loadparm.c:928 +msgid "Locking Options" +msgstr "" + +#: param/loadparm.c:930 +msgid "blocking locks" +msgstr "" + +#: param/loadparm.c:931 +msgid "fake oplocks" +msgstr "" + +#: param/loadparm.c:932 +msgid "kernel oplocks" +msgstr "" + +#: param/loadparm.c:933 +msgid "locking" +msgstr "" + +#: param/loadparm.c:935 +msgid "oplocks" +msgstr "" + +#: param/loadparm.c:936 +msgid "level2 oplocks" +msgstr "" + +#: param/loadparm.c:937 +msgid "oplock break wait time" +msgstr "" + +#: param/loadparm.c:938 +msgid "oplock contention limit" +msgstr "" + +#: param/loadparm.c:939 +msgid "posix locking" +msgstr "" + +#: param/loadparm.c:940 +msgid "strict locking" +msgstr "" + +#: param/loadparm.c:941 +msgid "share modes" +msgstr "" + +#: param/loadparm.c:944 +msgid "Ldap Options" +msgstr "" + +#: param/loadparm.c:946 +msgid "ldap server" +msgstr "" + +#: param/loadparm.c:947 +msgid "ldap port" +msgstr "" + +#: param/loadparm.c:948 +msgid "ldap suffix" +msgstr "" + +#: param/loadparm.c:949 +msgid "ldap filter" +msgstr "" + +#: param/loadparm.c:950 +msgid "ldap root" +msgstr "" + +#: param/loadparm.c:951 +msgid "ldap root passwd" +msgstr "" + +#: param/loadparm.c:954 +msgid "Miscellaneous Options" +msgstr "" + +#: param/loadparm.c:955 +msgid "add share command" +msgstr "" + +#: param/loadparm.c:956 +msgid "change share command" +msgstr "" + +#: param/loadparm.c:957 +msgid "delete share command" +msgstr "" + +#: param/loadparm.c:959 +msgid "config file" +msgstr "" + +#: param/loadparm.c:960 +msgid "preload" +msgstr "" + +#: param/loadparm.c:961 +msgid "auto services" +msgstr "" + +#: param/loadparm.c:962 +msgid "lock dir" +msgstr "" + +#: param/loadparm.c:963 +msgid "lock directory" +msgstr "" + +#: param/loadparm.c:965 +msgid "utmp directory" +msgstr "" + +#: param/loadparm.c:966 +msgid "wtmp directory" +msgstr "" + +#: param/loadparm.c:967 +msgid "utmp" +msgstr "" + +#: param/loadparm.c:970 +msgid "default service" +msgstr "" + +#: param/loadparm.c:971 +msgid "default" +msgstr "" + +#: param/loadparm.c:972 +msgid "message command" +msgstr "" + +#: param/loadparm.c:973 +msgid "dfree command" +msgstr "" + +#: param/loadparm.c:974 +msgid "remote announce" +msgstr "" + +#: param/loadparm.c:975 +msgid "remote browse sync" +msgstr "" + +#: param/loadparm.c:976 +msgid "socket address" +msgstr "" + +#: param/loadparm.c:977 +msgid "homedir map" +msgstr "" + +#: param/loadparm.c:978 +msgid "time offset" +msgstr "" + +#: param/loadparm.c:979 +msgid "NIS homedir" +msgstr "" + +#: param/loadparm.c:980 +msgid "-valid" +msgstr "" + +#: param/loadparm.c:982 +msgid "copy" +msgstr "" + +#: param/loadparm.c:983 +msgid "include" +msgstr "" + +#: param/loadparm.c:984 +msgid "exec" +msgstr "" + +#: param/loadparm.c:985 +msgid "preexec" +msgstr "" + +#: param/loadparm.c:987 +msgid "preexec close" +msgstr "" + +#: param/loadparm.c:988 +msgid "postexec" +msgstr "" + +#: param/loadparm.c:989 +msgid "root preexec" +msgstr "" + +#: param/loadparm.c:990 +msgid "root preexec close" +msgstr "" + +#: param/loadparm.c:991 +msgid "root postexec" +msgstr "" + +#: param/loadparm.c:992 +msgid "available" +msgstr "" + +#: param/loadparm.c:993 +msgid "volume" +msgstr "" + +#: param/loadparm.c:994 +msgid "fstype" +msgstr "" + +#: param/loadparm.c:995 +msgid "set directory" +msgstr "" + +#: param/loadparm.c:996 +msgid "source environment" +msgstr "" + +#: param/loadparm.c:997 +msgid "wide links" +msgstr "" + +#: param/loadparm.c:998 +msgid "follow symlinks" +msgstr "" + +#: param/loadparm.c:999 +msgid "dont descend" +msgstr "" + +#: param/loadparm.c:1000 +msgid "magic script" +msgstr "" + +#: param/loadparm.c:1001 +msgid "magic output" +msgstr "" + +#: param/loadparm.c:1002 +msgid "delete readonly" +msgstr "" + +#: param/loadparm.c:1003 +msgid "dos filemode" +msgstr "" + +#: param/loadparm.c:1004 +msgid "dos filetimes" +msgstr "" + +#: param/loadparm.c:1005 +msgid "dos filetime resolution" +msgstr "" + +#: param/loadparm.c:1007 +msgid "fake directory create times" +msgstr "" + +#: param/loadparm.c:1008 +msgid "panic action" +msgstr "" + +#: param/loadparm.c:1009 +msgid "hide local users" +msgstr "" + +#: param/loadparm.c:1012 +msgid "VFS options" +msgstr "" + +#: param/loadparm.c:1014 +msgid "vfs object" +msgstr "" + +#: param/loadparm.c:1015 +msgid "vfs options" +msgstr "" + +#: param/loadparm.c:1018 +msgid "msdfs root" +msgstr "" + +#: param/loadparm.c:1019 +msgid "host msdfs" +msgstr "" + +#: param/loadparm.c:1021 +msgid "Winbind options" +msgstr "" + +#: param/loadparm.c:1023 +msgid "winbind uid" +msgstr "" + +#: param/loadparm.c:1024 +msgid "winbind gid" +msgstr "" + +#: param/loadparm.c:1025 +msgid "template homedir" +msgstr "" + +#: param/loadparm.c:1026 +msgid "template shell" +msgstr "" + +#: param/loadparm.c:1027 +msgid "winbind separator" +msgstr "" + +#: param/loadparm.c:1028 +msgid "winbind cache time" +msgstr "" + +#: param/loadparm.c:1029 +msgid "winbind enum users" +msgstr "" + +#: param/loadparm.c:1030 +msgid "winbind enum groups" +msgstr "" diff --git a/source4/po/fr.msg b/source4/po/fr.msg new file mode 100644 index 0000000000..493db659ae --- /dev/null +++ b/source4/po/fr.msg @@ -0,0 +1,1709 @@ +# French messages for international release of SWAT. +# Copyright (C) 2001 François Le Lay + +msgid "" +msgstr "" +"Project-Id-Version: i18n_swat \n" +"POT-Creation-Date: 2001-09-20 14:05+0100\n" +"PO-Revision-Date: 2000-02-08 14:45+0100\n" +"Last-Translator: François Le Lay \n" +"Language-Team: (Samba Team) \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=US-ASCII\n" +"Content-Transfer-Encoding: \n" + +#: web/swat.c:120 +#, c-format +msgid "ERROR: Can't open %s\n" +msgstr "ERREUR: Impossible d'ouvrir %s\n" + +#. +#. str = stripspace(parm->label); +#. strlower (str); //monyo +#. d_printf("%s       %s", +#. str, _("Help"), parm->label); +#. +#: web/swat.c:211 +msgid "Help" +msgstr "Aide" + +#: web/swat.c:217 web/swat.c:231 web/swat.c:246 web/swat.c:254 web/swat.c:263 +#: web/swat.c:272 web/swat.c:278 web/swat.c:284 web/swat.c:297 +msgid "Set Default" +msgstr "Définir par défaut" + +#: web/swat.c:502 +#, c-format +msgid "Logged in as %s

\n" +msgstr "Connecté en tant que %s

\n" + +#: 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:512 +msgid "Status" +msgstr "Statut" + +#: web/swat.c:513 +msgid "View Config" +msgstr "Voir Configuration" + +#: web/swat.c:515 +msgid "Password Management" +msgstr "Gestion des mots de passe" + +#: web/swat.c:539 +msgid "Current Config" +msgstr "Configuration Actuelle" + +#: web/swat.c:543 +msgid "Normal View" +msgstr "Vue Normale" + +#: web/swat.c:545 +msgid "Full View" +msgstr "Vue Complète" + +#: web/swat.c:561 +msgid "Global Variables" +msgstr "Variables Globales" + +#: web/swat.c:575 web/swat.c:671 web/swat.c:1014 +msgid "Commit Changes" +msgstr "Sauver les modifications" + +#: web/swat.c:579 web/swat.c:674 web/swat.c:1016 +msgid "Reset Values" +msgstr "Réinitialiser Valeurs" + +#: web/swat.c:581 web/swat.c:676 web/swat.c:1018 +msgid "Advanced View" +msgstr "Vue Détaillée" + +#: web/swat.c:583 web/swat.c:678 web/swat.c:1020 +msgid "Basic View" +msgstr "Vue Basique" + +#: web/swat.c:613 +msgid "Share Parameters" +msgstr "Paramètres de partage" + +#: web/swat.c:642 +msgid "Choose Share" +msgstr "Choisir un partage" + +#: web/swat.c:656 +msgid "Delete Share" +msgstr "Supprimer un partage" + +#: web/swat.c:663 +msgid "Create Share" +msgstr "Créer un partage" + +#: web/swat.c:708 +msgid "password change in demo mode rejected\n" +msgstr "changement de mot de passe en mode démo rejeté" + +#: web/swat.c:747 +msgid " Must specify \"User Name\" \n" +msgstr " Le champ \"Nom d'utilisateur\" doit être spécifié\n" + +#: web/swat.c:763 +msgid " Must specify \"Old Password\" \n" +msgstr " Le champ \"Ancien mot de passe\" doît être spécifié\n" + +#: web/swat.c:769 +msgid " Must specify \"Remote Machine\" \n" +msgstr " Le champ \"Machine Distante\" doît être spécifié\n" + +#: web/swat.c:776 +msgid " Must specify \"New, and Re-typed Passwords\" \n" +msgstr " Les champs \"Nouveau mot de passe\" et \"Confirmation du nouveau mot de passe\" doivent être spécifiés \n" + +#: web/swat.c:782 +msgid " Re-typed password didn't match new password\n" +msgstr " Echec de la confirmation du nouveau mot de passe\n" + +#: web/swat.c:812 +#, c-format +msgid " The passwd for '%s' has been changed. \n" +msgstr " Le mot de passe de '%s' a été modifié. \n" + +#: web/swat.c:814 +#, c-format +msgid " The password for '%s' has NOT been changed. \n" +msgstr " Le mot de passe de '%s' n'a PAS été modifié. \n" + +#: web/swat.c:838 +msgid "Server Password Management" +msgstr "Gestion des mots de passe serveur" + +#. +#. * Create all the dialog boxes for data collection +#. +#: web/swat.c:847 web/swat.c:894 +msgid " User Name : " +msgstr " Nom d'utilisateur : " + +#: web/swat.c:850 web/swat.c:896 +msgid " Old Password : " +msgstr " Ancien mot de passe : " + +#: web/swat.c:853 web/swat.c:898 +msgid " New Password : " +msgstr " Nouveau mot de passe : " + +#: web/swat.c:855 web/swat.c:900 +msgid " Re-type New Password : " +msgstr " Confirmation du nouveau mot de passe : " + +#: web/swat.c:863 web/swat.c:911 +msgid "Change Password" +msgstr "Modifier le mot de passe" + +#: web/swat.c:866 +msgid "Add New User" +msgstr "Nouvel Utilisateur" + +#: web/swat.c:868 +msgid "Delete User" +msgstr "Supprimer Utilisateur" + +#: web/swat.c:870 +msgid "Disable User" +msgstr "Désactiver Utilisateur" + +#: web/swat.c:872 +msgid "Enable User" +msgstr "Activer Utilisateur" + +#: web/swat.c:885 +msgid "Client/Server Password Management" +msgstr "Gestion des mots de passe Client/Serveur" + +#: web/swat.c:902 +msgid " Remote Machine : " +msgstr " Machine distante : " + +#: web/swat.c:940 +msgid "Printer Parameters" +msgstr "Paramètres Imprimantes" + +#: web/swat.c:942 +msgid "Important Note:" +msgstr "Note Importante:" + +#: web/swat.c:943 +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:944 +msgid "are autoloaded printers from " +msgstr "désignent des imprimantes automatiquement chargées depuis le " + +#: web/swat.c:945 +msgid "Printcap Name" +msgstr "Nom Printcap" + +#: web/swat.c:946 +msgid "Attempting to delete these printers from SWAT will have no effect.\n" +msgstr "Essayer de supprimer ces imprimantes depuis SWAT n'aura aucun effet.\n" + +#: web/swat.c:980 +msgid "Choose Printer" +msgstr "Choisir Imprimante" + +#: web/swat.c:999 +msgid "Delete Printer" +msgstr "Supprimer Imprimante" + +#: web/swat.c:1006 +msgid "Create Printer" +msgstr "Créer Imprimante" + +#: web/statuspage.c:40 +msgid "DENY_NONE" +msgstr "" + +#: web/statuspage.c:41 +msgid "DENY_ALL " +msgstr "" + +#: web/statuspage.c:42 +msgid "DENY_DOS " +msgstr "" + +#: web/statuspage.c:43 +msgid "DENY_READ " +msgstr "" + +#: web/statuspage.c:44 +msgid "DENY_WRITE " +msgstr "" + +#: web/statuspage.c:50 +msgid "RDONLY " +msgstr "" + +#: web/statuspage.c:51 +msgid "WRONLY " +msgstr "" + +#: web/statuspage.c:52 +msgid "RDWR " +msgstr "" + +#: web/statuspage.c:60 +msgid "EXCLUSIVE+BATCH " +msgstr "" + +#: web/statuspage.c:62 +msgid "EXCLUSIVE " +msgstr "" + +#: web/statuspage.c:64 +msgid "BATCH " +msgstr "" + +#: web/statuspage.c:66 +msgid "LEVEL_II " +msgstr "" + +#: web/statuspage.c:68 +msgid "NONE " +msgstr "" + +#: web/statuspage.c:195 +msgid "Server Status" +msgstr "Statut du Serveur" + +#: web/statuspage.c:200 +msgid "Auto Refresh" +msgstr "Rafraîchissement Automatique" + +#: web/statuspage.c:201 web/statuspage.c:206 +msgid "Refresh Interval: " +msgstr "Intervalle de rafraîchissement: " + +#: web/statuspage.c:205 +msgid "Stop Refreshing" +msgstr "Stopper Rafraîchissement" + +#: web/statuspage.c:220 +msgid "version:" +msgstr "version:" + +#: web/statuspage.c:223 +msgid "smbd:" +msgstr "" + +#: web/statuspage.c:223 web/statuspage.c:235 +msgid "running" +msgstr "actif" + +#: web/statuspage.c:223 web/statuspage.c:235 +msgid "not running" +msgstr "non actif" + +#: web/statuspage.c:226 +msgid "Stop smbd" +msgstr "Stopper smbd" + +#: web/statuspage.c:228 +msgid "Start smbd" +msgstr "Lancer smbd" + +#: web/statuspage.c:230 +msgid "Restart smbd" +msgstr "Relancer smbd" + +#: web/statuspage.c:235 +msgid "nmbd:" +msgstr "" + +#: web/statuspage.c:238 +msgid "Stop nmbd" +msgstr "Stopper nmbd" + +#: web/statuspage.c:240 +msgid "Start nmbd" +msgstr "Lancer nmbd" + +#: web/statuspage.c:242 +msgid "Restart nmbd" +msgstr "Relancer nmbd" + +#: web/statuspage.c:249 +msgid "Active Connections" +msgstr "Connections Actives" + +#: web/statuspage.c:251 web/statuspage.c:264 web/statuspage.c:272 +msgid "PID" +msgstr "" + +#: web/statuspage.c:251 web/statuspage.c:264 +msgid "Client" +msgstr "" + +#: web/statuspage.c:251 +msgid "IP address" +msgstr "adresse IP" + +#: web/statuspage.c:251 web/statuspage.c:264 web/statuspage.c:272 +msgid "Date" +msgstr "Date" + +#: web/statuspage.c:253 +msgid "Kill" +msgstr "Terminer" + +#: web/statuspage.c:261 +msgid "Active Shares" +msgstr "Partages Actifs" + +#: web/statuspage.c:264 +msgid "Share" +msgstr "Partager" + +#: web/statuspage.c:264 +msgid "User" +msgstr "Utilisateur" + +#: web/statuspage.c:264 +msgid "Group" +msgstr "Groupe" + +#: web/statuspage.c:270 +msgid "Open Files" +msgstr "Fichiers Ouverts" + +#: web/statuspage.c:272 +msgid "Sharing" +msgstr "" + +#: web/statuspage.c:272 +msgid "R/W" +msgstr "" + +#: web/statuspage.c:272 +msgid "Oplock" +msgstr "" + +#: web/statuspage.c:272 +msgid "File" +msgstr "Fichier" + +#: param/loadparm.c:641 +msgid "Base Options" +msgstr "Options de base" + +#: param/loadparm.c:643 +msgid "dos charset" +msgstr "caractères dos" + +#: param/loadparm.c:644 +msgid "unix charset" +msgstr "caractères unix" + +#: param/loadparm.c:645 +msgid "display charset" +msgstr "caractères display" + +#: param/loadparm.c:646 +msgid "comment" +msgstr "commentaire" + +#: param/loadparm.c:647 +msgid "path" +msgstr "chemin" + +#: param/loadparm.c:648 +msgid "directory" +msgstr "répertoire" + +#: param/loadparm.c:649 +msgid "workgroup" +msgstr "groupe de travail" + +#: param/loadparm.c:650 +msgid "netbios name" +msgstr "nom netbios" + +#: param/loadparm.c:651 +msgid "netbios aliases" +msgstr "alias netbios" + +#: param/loadparm.c:652 +msgid "netbios scope" +msgstr "" + +#: param/loadparm.c:653 +msgid "server string" +msgstr "" + +#: param/loadparm.c:654 +msgid "interfaces" +msgstr "" + +#: param/loadparm.c:655 +msgid "bind interfaces only" +msgstr "lier uniquement les interfaces" + +#: param/loadparm.c:657 +msgid "Security Options" +msgstr "Options de Sécurité" + +#: param/loadparm.c:659 +msgid "security" +msgstr "sécurité" + +#: param/loadparm.c:660 +msgid "encrypt passwords" +msgstr "crypter les mots de passe" + +#: param/loadparm.c:661 +msgid "update encrypted" +msgstr "mise à jour cryptés" + +#: param/loadparm.c:662 +msgid "allow trusted domains" +msgstr "autoriser les domaines de confiance" + +#: param/loadparm.c:663 +msgid "alternate permissions" +msgstr "permissions alternatives" + +#: param/loadparm.c:664 +msgid "hosts equiv" +msgstr "" + +#: param/loadparm.c:665 +msgid "min passwd length" +msgstr "taille minimale des mots de passe" + +#: param/loadparm.c:666 +msgid "min password length" +msgstr "taille minimale des mots de passe" + +#: param/loadparm.c:667 +msgid "map to guest" +msgstr "associer à un invité" + +#: param/loadparm.c:668 +msgid "null passwords" +msgstr "mots de passe null" + +#: param/loadparm.c:669 +msgid "obey pam restrictions" +msgstr "utiliser les restrictions pam" + +#: param/loadparm.c:670 +msgid "password server" +msgstr "serveur de mots de passe" + +#: param/loadparm.c:671 +msgid "smb passwd file" +msgstr "fichier passwd de smb" + +#: param/loadparm.c:672 +msgid "private dir" +msgstr "répertoire privé" + +#: param/loadparm.c:673 +msgid "passdb module path" +msgstr "chemin du module passdb" + +#: param/loadparm.c:674 +msgid "root directory" +msgstr "répertoire root" + +#: param/loadparm.c:675 +msgid "root dir" +msgstr "répertoire root" + +#: param/loadparm.c:676 +msgid "root" +msgstr "" + +#: param/loadparm.c:678 +msgid "pam password change" +msgstr "modification de mot de passe pam" + +#: param/loadparm.c:679 +msgid "passwd program" +msgstr "programme passwd" + +#: param/loadparm.c:680 +msgid "passwd chat" +msgstr "" + +#: param/loadparm.c:681 +msgid "passwd chat debug" +msgstr "" + +#: param/loadparm.c:682 +msgid "username map" +msgstr "" + +#: param/loadparm.c:683 +msgid "password level" +msgstr "niveau mot de passe" + +#: param/loadparm.c:684 +msgid "username level" +msgstr "niveau nom d'utilisateur" + +#: param/loadparm.c:685 +msgid "unix password sync" +msgstr "synchroniser avec mots de passe unix" + +#: param/loadparm.c:686 +msgid "restrict anonymous" +msgstr "limiter anonyme" + +#: param/loadparm.c:687 +msgid "lanman auth" +msgstr "" + +#: param/loadparm.c:688 +msgid "ntlm auth" +msgstr "" + +#: param/loadparm.c:689 +msgid "plaintext to smbpasswd" +msgstr "texte brut vers smbpasswd" + +#: param/loadparm.c:690 +msgid "use rhosts" +msgstr "utiliser rhosts" + +#: param/loadparm.c:692 +msgid "username" +msgstr "nom d'utilisateur" + +#: param/loadparm.c:693 +msgid "user" +msgstr "utilisateur" + +#: param/loadparm.c:694 +msgid "users" +msgstr "utilisateurs" + +#: param/loadparm.c:696 +msgid "guest account" +msgstr "compte invité" + +#: param/loadparm.c:697 +msgid "invalid users" +msgstr "utilisateurs non-valides" + +#: param/loadparm.c:698 +msgid "valid users" +msgstr "utilisateurs valides" + +#: param/loadparm.c:699 +msgid "admin users" +msgstr "utilisateurs administrateurs" + +#: param/loadparm.c:700 +msgid "read list" +msgstr "" + +#: param/loadparm.c:701 +msgid "write list" +msgstr "" + +#: param/loadparm.c:702 +msgid "printer admin" +msgstr "administrateur d'imprimante" + +#: param/loadparm.c:703 +msgid "force user" +msgstr "forcer utilisateur" + +#: param/loadparm.c:704 +msgid "force group" +msgstr "forcer le groupe" + +#: param/loadparm.c:705 +msgid "group" +msgstr "groupe" + +#: param/loadparm.c:707 +msgid "read only" +msgstr "lecture seule" + +#: param/loadparm.c:708 +msgid "write ok" +msgstr "écriture ok" + +#: param/loadparm.c:709 +msgid "writeable" +msgstr "écriture possible" + +#: param/loadparm.c:710 +msgid "writable" +msgstr "écriture possible" + +#: param/loadparm.c:712 +msgid "create mask" +msgstr "" + +#: param/loadparm.c:713 +msgid "create mode" +msgstr "mode création" + +#: param/loadparm.c:714 +msgid "force create mode" +msgstr "forcer le mode création" + +#: param/loadparm.c:715 +msgid "security mask" +msgstr "" + +#: param/loadparm.c:716 +msgid "force security mode" +msgstr "forcer le mode sécurité" + +#: param/loadparm.c:717 +msgid "directory mask" +msgstr "masque de répertoire" + +#: param/loadparm.c:718 +msgid "directory mode" +msgstr "mode répertoire" + +#: param/loadparm.c:719 +msgid "force directory mode" +msgstr "forcer le mode répertoire" + +#: param/loadparm.c:720 +msgid "directory security mask" +msgstr "masque de sécurité répertoire" + +#: param/loadparm.c:721 +msgid "force directory security mode" +msgstr "forcer le mode sécurité répertoire" + +#: param/loadparm.c:722 +msgid "inherit permissions" +msgstr "héritage de permissions" + +#: param/loadparm.c:723 +msgid "guest only" +msgstr "invité seulement" + +#: param/loadparm.c:724 +msgid "only guest" +msgstr "invité seulement" + +#: param/loadparm.c:726 +msgid "guest ok" +msgstr "invité ok" + +#: param/loadparm.c:727 +msgid "public" +msgstr "public" + +#: param/loadparm.c:729 +msgid "only user" +msgstr "utilisateur seulement" + +#: param/loadparm.c:730 +msgid "hosts allow" +msgstr "autoriser hosts" + +#: param/loadparm.c:731 +msgid "allow hosts" +msgstr "autoriser hosts" + +#: param/loadparm.c:732 +msgid "hosts deny" +msgstr "refuser hosts" + +#: param/loadparm.c:733 +msgid "deny hosts" +msgstr "refuser hosts" + +#: param/loadparm.c:736 +msgid "Secure Socket Layer Options" +msgstr "Options Secure Socket Layer" + +#: param/loadparm.c:737 +msgid "ssl" +msgstr "" + +#: param/loadparm.c:739 +msgid "ssl hosts" +msgstr "" + +#: param/loadparm.c:740 +msgid "ssl hosts resign" +msgstr "" + +#: param/loadparm.c:741 +msgid "ssl CA certDir" +msgstr "" + +#: param/loadparm.c:742 +msgid "ssl CA certFile" +msgstr "" + +#: param/loadparm.c:743 +msgid "ssl server cert" +msgstr "certificat ssl serveur" + +#: param/loadparm.c:744 +msgid "ssl server key" +msgstr "clé ssl serveur" + +#: param/loadparm.c:745 +msgid "ssl client cert" +msgstr "certificat ssl client" + +#: param/loadparm.c:746 +msgid "ssl client key" +msgstr "clé ssl client" + +#: param/loadparm.c:747 +msgid "ssl require clientcert" +msgstr "ssl requiert un certificat client" + +#: param/loadparm.c:748 +msgid "ssl require servercert" +msgstr "ssl requiert un certificat serveur" + +#: param/loadparm.c:749 +msgid "ssl ciphers" +msgstr "chiffres ssl" + +#: param/loadparm.c:750 +msgid "ssl version" +msgstr "ssl version" + +#: param/loadparm.c:751 +msgid "ssl compatibility" +msgstr "compatibilité ssl" + +#: param/loadparm.c:754 +msgid "Logging Options" +msgstr "Options de Logging" + +#: param/loadparm.c:755 +msgid "log level" +msgstr "niveau log" + +#: param/loadparm.c:756 +msgid "debuglevel" +msgstr "niveau debug" + +#: param/loadparm.c:757 +msgid "syslog" +msgstr "" + +#: param/loadparm.c:758 +msgid "syslog only" +msgstr "syslog seulement" + +#: param/loadparm.c:759 +msgid "log file" +msgstr "fichier log" + +#: param/loadparm.c:761 +msgid "max log size" +msgstr "taille maxi de la log" + +#: param/loadparm.c:762 +msgid "timestamp logs" +msgstr "" + +#: param/loadparm.c:763 +msgid "debug timestamp" +msgstr "" + +#: param/loadparm.c:764 +msgid "debug hires timestamp" +msgstr "" + +#: param/loadparm.c:765 +msgid "debug pid" +msgstr "" + +#: param/loadparm.c:766 +msgid "debug uid" +msgstr "" + +#: param/loadparm.c:768 +msgid "Protocol Options" +msgstr "Options de Protocole" + +#: param/loadparm.c:770 +msgid "protocol" +msgstr "protocole" + +#: param/loadparm.c:771 +msgid "large readwrite" +msgstr "" + +#: param/loadparm.c:772 +msgid "max protocol" +msgstr "protocole maxi" + +#: param/loadparm.c:773 +msgid "min protocol" +msgstr "protocole mini" + +#: param/loadparm.c:774 +msgid "unicode" +msgstr "" + +#: param/loadparm.c:775 +msgid "read bmpx" +msgstr "" + +#: param/loadparm.c:776 +msgid "read raw" +msgstr "" + +#: param/loadparm.c:777 +msgid "write raw" +msgstr "" + +#: param/loadparm.c:779 +msgid "nt smb support" +msgstr "support smb nt" + +#: param/loadparm.c:780 +msgid "nt pipe support" +msgstr "support pipe nt" + +#: param/loadparm.c:781 +msgid "nt acl support" +msgstr "support acl nt" + +#: param/loadparm.c:782 +msgid "announce version" +msgstr "annoncer la version" + +#: param/loadparm.c:783 +msgid "announce as" +msgstr "annoncer comme" + +#: param/loadparm.c:784 +msgid "max mux" +msgstr "mux maxi" + +#: param/loadparm.c:785 +msgid "max xmit" +msgstr "xmit maxi" + +#: param/loadparm.c:787 +msgid "name resolve order" +msgstr "ordre de résolution des noms" + +#: param/loadparm.c:788 +msgid "max packet" +msgstr "paquet maxi" + +#: param/loadparm.c:789 +msgid "packet size" +msgstr "taille de paquet" + +#: param/loadparm.c:790 +msgid "max ttl" +msgstr "ttl maxi" + +#: param/loadparm.c:791 +msgid "max wins ttl" +msgstr "wins ttl maxi" + +#: param/loadparm.c:792 +msgid "min wins ttl" +msgstr "wins ttl mini" + +#: param/loadparm.c:793 +msgid "time server" +msgstr "" + +#: param/loadparm.c:795 +msgid "Tuning Options" +msgstr "Options de réglage" + +#: param/loadparm.c:797 +msgid "change notify timeout" +msgstr "" + +#: param/loadparm.c:798 +msgid "deadtime" +msgstr "" + +#: param/loadparm.c:799 +msgid "getwd cache" +msgstr "" + +#: param/loadparm.c:800 +msgid "keepalive" +msgstr "" + +#: param/loadparm.c:802 +msgid "lpq cache time" +msgstr "" + +#: param/loadparm.c:803 +msgid "max smbd processes" +msgstr "nombre maxi de processus smbd" + +#: param/loadparm.c:804 +msgid "max connections" +msgstr "connections maxi" + +#: param/loadparm.c:805 +msgid "paranoid server security" +msgstr "" + +#: param/loadparm.c:806 +msgid "max disk size" +msgstr "taille maxi de disque" + +#: param/loadparm.c:807 +msgid "max open files" +msgstr "nombre maxi de fichiers ouverts" + +#: param/loadparm.c:808 +msgid "min print space" +msgstr "espace d'impression mini" + +#: param/loadparm.c:809 +msgid "read size" +msgstr "" + +#: param/loadparm.c:811 +msgid "socket options" +msgstr "options de socket" + +#: param/loadparm.c:812 +msgid "stat cache size" +msgstr "taille du cache stat" + +#: param/loadparm.c:813 +msgid "strict allocate" +msgstr "allocation stricte" + +#: param/loadparm.c:814 +msgid "strict sync" +msgstr "synchronisation stricte" + +#: param/loadparm.c:815 +msgid "sync always" +msgstr "toujours synchroniser" + +#: param/loadparm.c:816 +msgid "use mmap" +msgstr "utiliser mmap" + +#: param/loadparm.c:817 +msgid "hostname lookups" +msgstr "" + +#: param/loadparm.c:818 +msgid "write cache size" +msgstr "taille du cache d'écriture" + +#: param/loadparm.c:820 +msgid "Printing Options" +msgstr "Options d'impression" + +#: param/loadparm.c:822 +msgid "total print jobs" +msgstr "total jobs d'impression" + +#: param/loadparm.c:823 +msgid "max print jobs" +msgstr "max jobs d'impression" + +#: param/loadparm.c:824 +msgid "load printers" +msgstr "charger imprimantes" + +#: param/loadparm.c:825 +msgid "printcap name" +msgstr "nom printcap" + +#: param/loadparm.c:826 +msgid "printcap" +msgstr "" + +#: param/loadparm.c:827 +msgid "printable" +msgstr "imprimable" + +#: param/loadparm.c:828 +msgid "print ok" +msgstr "imprimante ok" + +#: param/loadparm.c:829 +msgid "postscript" +msgstr "" + +#: param/loadparm.c:830 +msgid "printing" +msgstr "impression" + +#: param/loadparm.c:831 +msgid "print command" +msgstr "commande d'impression" + +#: param/loadparm.c:832 +msgid "disable spoolss" +msgstr "désactiver spoolss" + +#: param/loadparm.c:833 +msgid "lpq command" +msgstr "commande lpq" + +#: param/loadparm.c:834 +msgid "lprm command" +msgstr "commande lprm" + +#: param/loadparm.c:835 +msgid "lppause command" +msgstr "commande lppause" + +#: param/loadparm.c:836 +msgid "lpresume command" +msgstr "commande lpresume" + +#: param/loadparm.c:837 +msgid "queuepause command" +msgstr "commande queuepause" + +#: param/loadparm.c:838 +msgid "queueresume command" +msgstr "commande queueresume" + +#: param/loadparm.c:840 +msgid "enumports command" +msgstr "commande enumports" + +#: param/loadparm.c:841 +msgid "addprinter command" +msgstr "commande addprinter" + +#: param/loadparm.c:842 +msgid "deleteprinter command" +msgstr "commande deleteprinter" + +#: param/loadparm.c:843 +msgid "show add printer wizard" +msgstr "Voir l'assistant d'ajout d'imprimante" + +#: param/loadparm.c:844 +msgid "os2 driver map" +msgstr "" + +#: param/loadparm.c:846 +msgid "printer name" +msgstr "nom d'imprimante" + +#: param/loadparm.c:847 +msgid "printer" +msgstr "imprimante" + +#: param/loadparm.c:848 +msgid "use client driver" +msgstr "utiliser le pilote client" + +#: param/loadparm.c:849 +msgid "printer driver" +msgstr "pilote d'imprimante" + +#: param/loadparm.c:850 +msgid "printer driver file" +msgstr "fichier de pilote d'imprimante" + +#: param/loadparm.c:851 +msgid "printer driver location" +msgstr "adresse du pilote d'imprimante" + +#: param/loadparm.c:853 +msgid "Filename Handling" +msgstr "Gestion des noms de fichier" + +#: param/loadparm.c:854 +msgid "strip dot" +msgstr "" + +#: param/loadparm.c:856 +msgid "mangled stack" +msgstr "" + +#: param/loadparm.c:857 +msgid "default case" +msgstr "casse par défaut" + +#: param/loadparm.c:858 +msgid "case sensitive" +msgstr "sensible à la casse" + +#: param/loadparm.c:859 +msgid "casesignames" +msgstr "" + +#: param/loadparm.c:860 +msgid "preserve case" +msgstr "garder la casse" + +#: param/loadparm.c:861 +msgid "short preserve case" +msgstr "" + +#: param/loadparm.c:862 +msgid "mangle case" +msgstr "" + +#: param/loadparm.c:863 +msgid "mangling char" +msgstr "" + +#: param/loadparm.c:864 +msgid "hide dot files" +msgstr "" + +#: param/loadparm.c:865 +msgid "hide unreadable" +msgstr "" + +#: param/loadparm.c:866 +msgid "delete veto files" +msgstr "supprimer les fichiers veto" + +#: param/loadparm.c:867 +msgid "veto files" +msgstr "fichiers veto" + +#: param/loadparm.c:868 +msgid "hide files" +msgstr "cacher les fichiers" + +#: param/loadparm.c:869 +msgid "veto oplock files" +msgstr "" + +#: param/loadparm.c:870 +msgid "map system" +msgstr "map système" + +#: param/loadparm.c:871 +msgid "map hidden" +msgstr "map caché" + +#: param/loadparm.c:872 +msgid "map archive" +msgstr "" + +#: param/loadparm.c:873 +msgid "mangled names" +msgstr "" + +#: param/loadparm.c:874 +msgid "mangled map" +msgstr "" + +#: param/loadparm.c:875 +msgid "stat cache" +msgstr "cache stat" + +#: param/loadparm.c:877 +msgid "Domain Options" +msgstr "Options de Domaine" + +#: param/loadparm.c:879 +msgid "domain admin group" +msgstr "groupe d'administration du domaine" + +#: param/loadparm.c:880 +msgid "domain guest group" +msgstr "groupe invité du domaine" + +#: param/loadparm.c:883 +msgid "groupname map" +msgstr "map noms de groupe" + +#: param/loadparm.c:886 +msgid "machine password timeout" +msgstr "" + +#: param/loadparm.c:888 +msgid "Logon Options" +msgstr "Options de Logon" + +#: param/loadparm.c:890 +msgid "add user script" +msgstr "ajouter script utilisateur" + +#: param/loadparm.c:891 +msgid "delete user script" +msgstr "supprimer script utilisateur" + +#: param/loadparm.c:892 +msgid "add group script" +msgstr "ajouter script de groupe" + +#: param/loadparm.c:893 +msgid "delete group script" +msgstr "supprimer script de groupe" + +#: param/loadparm.c:894 +msgid "add user to group script" +msgstr "ajouter un utilisateur à un script de groupe" + +#: param/loadparm.c:895 +msgid "delete user from group script" +msgstr "supprimer un utilisateur d'un script de groupe" + +#: param/loadparm.c:896 +msgid "add machine script" +msgstr "ajouter un script machine" + +#: param/loadparm.c:897 +msgid "shutdown script" +msgstr "script shutdown" + +#: param/loadparm.c:898 +msgid "abort shutdown script" +msgstr "annuler script shutdown" + +#: param/loadparm.c:900 +msgid "logon script" +msgstr "script de logon" + +#: param/loadparm.c:901 +msgid "logon path" +msgstr "" + +#: param/loadparm.c:902 +msgid "logon drive" +msgstr "" + +#: param/loadparm.c:903 +msgid "logon home" +msgstr "" + +#: param/loadparm.c:904 +msgid "domain logons" +msgstr "" + +#: param/loadparm.c:906 +msgid "Browse Options" +msgstr "Options de Navigation" + +#: param/loadparm.c:908 +msgid "os level" +msgstr "niveau os" + +#: param/loadparm.c:909 +msgid "lm announce" +msgstr "annonce lm" + +#: param/loadparm.c:910 +msgid "lm interval" +msgstr "intervalle lm" + +#: param/loadparm.c:911 +msgid "preferred master" +msgstr "master préféré" + +#: param/loadparm.c:912 +msgid "prefered master" +msgstr "master préféré" + +#: param/loadparm.c:913 +msgid "local master" +msgstr "master local" + +#: param/loadparm.c:914 +msgid "domain master" +msgstr "master de domaine" + +#: param/loadparm.c:915 +msgid "browse list" +msgstr "parcourir la liste" + +#: param/loadparm.c:916 +msgid "browseable" +msgstr "navigable" + +#: param/loadparm.c:917 +msgid "browsable" +msgstr "navigable" + +#: param/loadparm.c:918 +msgid "enhanced browsing" +msgstr "navigation améliorée" + +#: param/loadparm.c:920 +msgid "WINS Options" +msgstr "Options WINS" + +#: param/loadparm.c:921 +msgid "dns proxy" +msgstr "" + +#: param/loadparm.c:922 +msgid "wins proxy" +msgstr "" + +#: param/loadparm.c:924 +msgid "wins server" +msgstr "" + +#: param/loadparm.c:925 +msgid "wins support" +msgstr "" + +#: param/loadparm.c:926 +msgid "wins hook" +msgstr "" + +#: param/loadparm.c:928 +msgid "Locking Options" +msgstr "Options de Verrouillage" + +#: param/loadparm.c:930 +msgid "blocking locks" +msgstr "verrous bloquants" + +#: param/loadparm.c:931 +msgid "fake oplocks" +msgstr "" + +#: param/loadparm.c:932 +msgid "kernel oplocks" +msgstr "" + +#: param/loadparm.c:933 +msgid "locking" +msgstr "verrouillage" + +#: param/loadparm.c:935 +msgid "oplocks" +msgstr "" + +#: param/loadparm.c:936 +msgid "level2 oplocks" +msgstr "" + +#: param/loadparm.c:937 +msgid "oplock break wait time" +msgstr "" + +#: param/loadparm.c:938 +msgid "oplock contention limit" +msgstr "" + +#: param/loadparm.c:939 +msgid "posix locking" +msgstr "verrouillage posix" + +#: param/loadparm.c:940 +msgid "strict locking" +msgstr "verrouillage strict" + +#: param/loadparm.c:941 +msgid "share modes" +msgstr "modes de partage" + +#: param/loadparm.c:944 +msgid "Ldap Options" +msgstr "Options Ldap" + +#: param/loadparm.c:946 +msgid "ldap server" +msgstr "serveur ldap" + +#: param/loadparm.c:947 +msgid "ldap port" +msgstr "port ldap" + +#: param/loadparm.c:948 +msgid "ldap suffix" +msgstr "suffixe ldap" + +#: param/loadparm.c:949 +msgid "ldap filter" +msgstr "filtre ldap" + +#: param/loadparm.c:950 +msgid "ldap root" +msgstr "" + +#: param/loadparm.c:951 +msgid "ldap root passwd" +msgstr "" + +#: param/loadparm.c:954 +msgid "Miscellaneous Options" +msgstr "Options Diverses" + +#: param/loadparm.c:955 +msgid "add share command" +msgstr "ajouter une commande de partage" + +#: param/loadparm.c:956 +msgid "change share command" +msgstr "modifier une commande de partage" + +#: param/loadparm.c:957 +msgid "delete share command" +msgstr "supprimer une commande de partage" + +#: param/loadparm.c:959 +msgid "config file" +msgstr "fichier de configuration" + +#: param/loadparm.c:960 +msgid "preload" +msgstr "pré-chargement" + +#: param/loadparm.c:961 +msgid "auto services" +msgstr "" + +#: param/loadparm.c:962 +msgid "lock dir" +msgstr "" + +#: param/loadparm.c:963 +msgid "lock directory" +msgstr "" + +#: param/loadparm.c:965 +msgid "utmp directory" +msgstr "" + +#: param/loadparm.c:966 +msgid "wtmp directory" +msgstr "" + +#: param/loadparm.c:967 +msgid "utmp" +msgstr "" + +#: param/loadparm.c:970 +msgid "default service" +msgstr "service par défaut" + +#: param/loadparm.c:971 +msgid "default" +msgstr "par défaut" + +#: param/loadparm.c:972 +msgid "message command" +msgstr "commande message" + +#: param/loadparm.c:973 +msgid "dfree command" +msgstr "commande dfree" + +#: param/loadparm.c:974 +msgid "remote announce" +msgstr "annonce distante" + +#: param/loadparm.c:975 +msgid "remote browse sync" +msgstr "synchronisation de navigation distante" + +#: param/loadparm.c:976 +msgid "socket address" +msgstr "adresse de socket" + +#: param/loadparm.c:977 +msgid "homedir map" +msgstr "" + +#: param/loadparm.c:978 +msgid "time offset" +msgstr "" + +#: param/loadparm.c:979 +msgid "NIS homedir" +msgstr "" + +#: param/loadparm.c:980 +msgid "-valid" +msgstr "" + +#: param/loadparm.c:982 +msgid "copy" +msgstr "" + +#: param/loadparm.c:983 +msgid "include" +msgstr "" + +#: param/loadparm.c:984 +msgid "exec" +msgstr "" + +#: param/loadparm.c:985 +msgid "preexec" +msgstr "" + +#: param/loadparm.c:987 +msgid "preexec close" +msgstr "" + +#: param/loadparm.c:988 +msgid "postexec" +msgstr "" + +#: param/loadparm.c:989 +msgid "root preexec" +msgstr "" + +#: param/loadparm.c:990 +msgid "root preexec close" +msgstr "" + +#: param/loadparm.c:991 +msgid "root postexec" +msgstr "" + +#: param/loadparm.c:992 +msgid "available" +msgstr "disponible" + +#: param/loadparm.c:993 +msgid "volume" +msgstr "" + +#: param/loadparm.c:994 +msgid "fstype" +msgstr "" + +#: param/loadparm.c:995 +msgid "set directory" +msgstr "" + +#: param/loadparm.c:996 +msgid "source environment" +msgstr "" + +#: param/loadparm.c:997 +msgid "wide links" +msgstr "" + +#: param/loadparm.c:998 +msgid "follow symlinks" +msgstr "" + +#: param/loadparm.c:999 +msgid "dont descend" +msgstr "ne pas descendre" + +#: param/loadparm.c:1000 +msgid "magic script" +msgstr "" + +#: param/loadparm.c:1001 +msgid "magic output" +msgstr "" + +#: param/loadparm.c:1002 +msgid "delete readonly" +msgstr "supprimer lecture seule" + +#: param/loadparm.c:1003 +msgid "dos filemode" +msgstr "" + +#: param/loadparm.c:1004 +msgid "dos filetimes" +msgstr "" + +#: param/loadparm.c:1005 +msgid "dos filetime resolution" +msgstr "" + +#: param/loadparm.c:1007 +msgid "fake directory create times" +msgstr "" + +#: param/loadparm.c:1008 +msgid "panic action" +msgstr "" + +#: param/loadparm.c:1009 +msgid "hide local users" +msgstr "cacher les utilisateurs locaux" + +#: param/loadparm.c:1012 +msgid "VFS options" +msgstr "Options VFS" + +#: param/loadparm.c:1014 +msgid "vfs object" +msgstr "" + +#: param/loadparm.c:1015 +msgid "vfs options" +msgstr "" + +#: param/loadparm.c:1018 +msgid "msdfs root" +msgstr "" + +#: param/loadparm.c:1019 +msgid "host msdfs" +msgstr "" + +#: param/loadparm.c:1021 +msgid "Winbind options" +msgstr "Options Winbind" + +#: param/loadparm.c:1023 +msgid "winbind uid" +msgstr "" + +#: param/loadparm.c:1024 +msgid "winbind gid" +msgstr "" + +#: param/loadparm.c:1025 +msgid "template homedir" +msgstr "" + +#: param/loadparm.c:1026 +msgid "template shell" +msgstr "" + +#: param/loadparm.c:1027 +msgid "winbind separator" +msgstr "" + +#: param/loadparm.c:1028 +msgid "winbind cache time" +msgstr "" + +#: param/loadparm.c:1029 +msgid "winbind enum users" +msgstr "" + +#: param/loadparm.c:1030 +msgid "winbind enum groups" +msgstr "" + + diff --git a/source4/po/it.msg b/source4/po/it.msg new file mode 100644 index 0000000000..708f2165ee --- /dev/null +++ b/source4/po/it.msg @@ -0,0 +1,1707 @@ +# Italian messages for international release of SWAT. +# Copyright (C) 2001 Simo Sorce + +msgid "" +msgstr "" +"Project-Id-Version: i18n_swat \n" +"POT-Creation-Date: 2001-09-20 14:05+0100\n" +"PO-Revision-Date: 2000-02-08 14:45+0100\n" +"Last-Translator: Simo Sorce \n" +"Language-Team: (Samba Team) \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=US-ASCII\n" +"Content-Transfer-Encoding: \n" + +#: web/swat.c:120 +#, c-format +msgid "ERROR: Can't open %s\n" +msgstr "ERRORE: Impossibile aprire %s\n" + +#. +#. str = stripspace(parm->label); +#. strlower (str); //monyo +#. d_printf("%s       %s", +#. str, _("Help"), parm->label); +#. +#: web/swat.c:211 +msgid "Help" +msgstr "Aiuto" + +#: web/swat.c:217 web/swat.c:231 web/swat.c:246 web/swat.c:254 web/swat.c:263 +#: web/swat.c:272 web/swat.c:278 web/swat.c:284 web/swat.c:297 +msgid "Set Default" +msgstr "Imposta Default" + +#: web/swat.c:502 +#, c-format +msgid "Logged in as %s

\n" +msgstr "Connesso come %s

\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:512 +msgid "Status" +msgstr "Stato" + +#: web/swat.c:513 +msgid "View Config" +msgstr "Visualizza Configurazione" + +#: web/swat.c:515 +msgid "Password Management" +msgstr "Gestione Password" + +#: web/swat.c:539 +msgid "Current Config" +msgstr "Configurazione Attuale" + +#: web/swat.c:543 +msgid "Normal View" +msgstr "Vista Normale" + +#: web/swat.c:545 +msgid "Full View" +msgstr "Vista Completa" + +#: web/swat.c:561 +msgid "Global Variables" +msgstr "Variabili Globali" + +#: web/swat.c:575 web/swat.c:671 web/swat.c:1014 +msgid "Commit Changes" +msgstr "Salva Modifiche" + +#: web/swat.c:579 web/swat.c:674 web/swat.c:1016 +msgid "Reset Values" +msgstr "Resetta Valori" + +#: web/swat.c:581 web/swat.c:676 web/swat.c:1018 +msgid "Advanced View" +msgstr "Vista Avanzata" + +#: web/swat.c:583 web/swat.c:678 web/swat.c:1020 +msgid "Basic View" +msgstr "Vista Semplice" + +#: web/swat.c:613 +msgid "Share Parameters" +msgstr "Parametri Condivisioni" + +#: web/swat.c:642 +msgid "Choose Share" +msgstr "Scegli Condivisione" + +#: web/swat.c:656 +msgid "Delete Share" +msgstr "Cancella Condivisione" + +#: web/swat.c:663 +msgid "Create Share" +msgstr "Crea Condivisione" + +#: web/swat.c:708 +msgid "password change in demo mode rejected\n" +msgstr "cambio password in modalita' demo rigettata" + +#: web/swat.c:747 +msgid " Must specify \"User Name\" \n" +msgstr " \"Nome Utente\" deve essere specificato \n" + +#: web/swat.c:763 +msgid " Must specify \"Old Password\" \n" +msgstr " \"Vecchia Password\" deve essere specificato \n" + +#: web/swat.c:769 +msgid " Must specify \"Remote Machine\" \n" +msgstr " \"Macchina Remota\" deve essere specificato \n" + +#: web/swat.c:776 +msgid " Must specify \"New, and Re-typed Passwords\" \n" +msgstr " "Nuova/Conferma Password" devono essere specificati \n" + +#: web/swat.c:782 +msgid " Re-typed password didn't match new password\n" +msgstr " la password di conferma non e' uguale alla nuova password\n" + +#: web/swat.c:812 +#, c-format +msgid " The passwd for '%s' has been changed. \n" +msgstr " La password per '%s' e' stata cambiata. \n" + +#: web/swat.c:814 +#, c-format +msgid " The passwd for '%s' has NOT been changed. \n" +msgstr " La password per '%s' non e' stata cambianta. \n" + +#: web/swat.c:838 +msgid "Server Password Management" +msgstr "Gestione Password del Server" + +#. +#. * Create all the dialog boxes for data collection +#. +#: web/swat.c:847 web/swat.c:894 +msgid " User Name : " +msgstr " Nome Utente : " + +#: web/swat.c:850 web/swat.c:896 +msgid " Old Password : " +msgstr " Vecchia Password : " + +#: web/swat.c:853 web/swat.c:898 +msgid " New Password : " +msgstr " Nuova Password : " + +#: web/swat.c:855 web/swat.c:900 +msgid " Re-type New Password : " +msgstr " Conferma nuova Password : " + +#: web/swat.c:863 web/swat.c:911 +msgid "Change Password" +msgstr "Cambia Password" + +#: web/swat.c:866 +msgid "Add New User" +msgstr "Aggiungi Nuovo Utente" + +#: web/swat.c:868 +msgid "Delete User" +msgstr "Cancella Utente" + +#: web/swat.c:870 +msgid "Disable User" +msgstr "Disabilita Utente" + +#: web/swat.c:872 +msgid "Enable User" +msgstr "Abilita Utente" + +#: web/swat.c:885 +msgid "Client/Server Password Management" +msgstr "Gestione Password Client/Server" + +#: web/swat.c:902 +msgid " Remote Machine : " +msgstr " Macchina Remota : " + +#: web/swat.c:940 +msgid "Printer Parameters" +msgstr "Parametri Stampante" + +#: web/swat.c:942 +msgid "Important Note:" +msgstr "Nota Importante:" + +#: web/swat.c:943 +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:944 +msgid "are autoloaded printers from " +msgstr "sono stampanti caricate automaticamente da " + +#: web/swat.c:945 +msgid "Printcap Name" +msgstr "Nome Printcap" + +#: web/swat.c:946 +msgid "Attempting to delete these printers from SWAT will have no effect.\n" +msgstr "Il tentativo di cancellare queste stampanti da sWAT non avara' effetto.\n" + +#: web/swat.c:980 +msgid "Choose Printer" +msgstr "Scegli Stampante" + +#: web/swat.c:999 +msgid "Delete Printer" +msgstr "Cancella Stampante" + +#: web/swat.c:1006 +msgid "Create Printer" +msgstr "Crea Stampante" + +#: web/statuspage.c:40 +msgid "DENY_NONE" +msgstr "" + +#: web/statuspage.c:41 +msgid "DENY_ALL " +msgstr "" + +#: web/statuspage.c:42 +msgid "DENY_DOS " +msgstr "" + +#: web/statuspage.c:43 +msgid "DENY_READ " +msgstr "" + +#: web/statuspage.c:44 +msgid "DENY_WRITE " +msgstr "" + +#: web/statuspage.c:50 +msgid "RDONLY " +msgstr "" + +#: web/statuspage.c:51 +msgid "WRONLY " +msgstr "" + +#: web/statuspage.c:52 +msgid "RDWR " +msgstr "" + +#: web/statuspage.c:60 +msgid "EXCLUSIVE+BATCH " +msgstr "" + +#: web/statuspage.c:62 +msgid "EXCLUSIVE " +msgstr "" + +#: web/statuspage.c:64 +msgid "BATCH " +msgstr "" + +#: web/statuspage.c:66 +msgid "LEVEL_II " +msgstr "" + +#: web/statuspage.c:68 +msgid "NONE " +msgstr "" + +#: web/statuspage.c:195 +msgid "Server Status" +msgstr "Stato del Server" + +#: web/statuspage.c:200 +msgid "Auto Refresh" +msgstr "Rinfresco Automatico" + +#: web/statuspage.c:201 web/statuspage.c:206 +msgid "Refresh Interval: " +msgstr "Intervallo Rinfresco: " + +#: web/statuspage.c:205 +msgid "Stop Refreshing" +msgstr "Ferma Rinfresco" + +#: web/statuspage.c:220 +msgid "version:" +msgstr "versione:" + +#: web/statuspage.c:223 +msgid "smbd:" +msgstr "" + +#: web/statuspage.c:223 web/statuspage.c:235 +msgid "running" +msgstr "attivo" + +#: web/statuspage.c:223 web/statuspage.c:235 +msgid "not running" +msgstr "non attivo" + +#: web/statuspage.c:226 +msgid "Stop smbd" +msgstr "Ferma smbd" + +#: web/statuspage.c:228 +msgid "Start smbd" +msgstr "Lancia smbd" + +#: web/statuspage.c:230 +msgid "Restart smbd" +msgstr "Rilancia smbd" + +#: web/statuspage.c:235 +msgid "nmbd:" +msgstr "" + +#: web/statuspage.c:238 +msgid "Stop nmbd" +msgstr "Ferma nmbd" + +#: web/statuspage.c:240 +msgid "Start nmbd" +msgstr "Lancia nmbd" + +#: web/statuspage.c:242 +msgid "Restart nmbd" +msgstr "Rilancia nmbd" + +#: web/statuspage.c:249 +msgid "Active Connections" +msgstr "Connessioni Attive" + +#: web/statuspage.c:251 web/statuspage.c:264 web/statuspage.c:272 +msgid "PID" +msgstr "" + +#: web/statuspage.c:251 web/statuspage.c:264 +msgid "Client" +msgstr "" + +#: web/statuspage.c:251 +msgid "IP address" +msgstr "indirizzo IP" + +#: web/statuspage.c:251 web/statuspage.c:264 web/statuspage.c:272 +msgid "Date" +msgstr "Data" + +#: web/statuspage.c:253 +msgid "Kill" +msgstr "Termina" + +#: web/statuspage.c:261 +msgid "Active Shares" +msgstr "Condivisioni Attive" + +#: web/statuspage.c:264 +msgid "Share" +msgstr "Condivisione" + +#: web/statuspage.c:264 +msgid "User" +msgstr "Utente" + +#: web/statuspage.c:264 +msgid "Group" +msgstr "Gruppo" + +#: web/statuspage.c:270 +msgid "Open Files" +msgstr "File Aperti" + +#: web/statuspage.c:272 +msgid "Sharing" +msgstr "" + +#: web/statuspage.c:272 +msgid "R/W" +msgstr "" + +#: web/statuspage.c:272 +msgid "Oplock" +msgstr "" + +#: web/statuspage.c:272 +msgid "File" +msgstr "" + +#: param/loadparm.c:641 +msgid "Base Options" +msgstr "Opzioni Basilari" + +#: param/loadparm.c:643 +msgid "dos charset" +msgstr "set caratteri dos" + +#: param/loadparm.c:644 +msgid "unix charset" +msgstr "set caratteri unix" + +#: param/loadparm.c:645 +msgid "display charset" +msgstr "set caratteri display" + +#: param/loadparm.c:646 +msgid "comment" +msgstr "commento" + +#: param/loadparm.c:647 +msgid "path" +msgstr "percorso" + +#: param/loadparm.c:648 +msgid "directory" +msgstr "" + +#: param/loadparm.c:649 +msgid "workgroup" +msgstr "" + +#: param/loadparm.c:650 +msgid "netbios name" +msgstr "nome netbios" + +#: param/loadparm.c:651 +msgid "netbios aliases" +msgstr "alias netbios" + +#: param/loadparm.c:652 +msgid "netbios scope" +msgstr "scope netbios" + +#: param/loadparm.c:653 +msgid "server string" +msgstr "stringa server" + +#: param/loadparm.c:654 +msgid "interfaces" +msgstr "interfacce" + +#: param/loadparm.c:655 +msgid "bind interfaces only" +msgstr "usa solo interfacce definite" + +#: param/loadparm.c:657 +msgid "Security Options" +msgstr "Opzioni di Sicurezza" + +#: param/loadparm.c:659 +msgid "security" +msgstr "sicurezza" + +#: param/loadparm.c:660 +msgid "encrypt passwords" +msgstr "password cryptate" + +#: param/loadparm.c:661 +msgid "update encrypted" +msgstr "aggiorna criptate" + +#: param/loadparm.c:662 +msgid "allow trusted domains" +msgstr "permetti domini trusted" + +#: param/loadparm.c:663 +msgid "alternate permissions" +msgstr "permessi alternativi" + +#: param/loadparm.c:664 +msgid "hosts equiv" +msgstr "" + +#: param/loadparm.c:665 +msgid "min passwd length" +msgstr "minima lunghezza passwd" + +#: param/loadparm.c:666 +msgid "min password length" +msgstr "minima lunghezza password" + +#: param/loadparm.c:667 +msgid "map to guest" +msgstr "mappa su ospite" + +#: param/loadparm.c:668 +msgid "null passwords" +msgstr "password nulle" + +#: param/loadparm.c:669 +msgid "obey pam restrictions" +msgstr "usa restrizioni pam" + +#: param/loadparm.c:670 +msgid "password server" +msgstr "" + +#: param/loadparm.c:671 +msgid "smb passwd file" +msgstr "" + +#: param/loadparm.c:672 +msgid "private dir" +msgstr "directory privata" + +#: param/loadparm.c:673 +msgid "passdb module path" +msgstr "percorso modulo passdb" + +#: param/loadparm.c:674 +msgid "root directory" +msgstr "" + +#: param/loadparm.c:675 +msgid "root dir" +msgstr "" + +#: param/loadparm.c:676 +msgid "root" +msgstr "" + +#: param/loadparm.c:678 +msgid "pam password change" +msgstr "" + +#: param/loadparm.c:679 +msgid "passwd program" +msgstr "" + +#: param/loadparm.c:680 +msgid "passwd chat" +msgstr "" + +#: param/loadparm.c:681 +msgid "passwd chat debug" +msgstr "" + +#: param/loadparm.c:682 +msgid "username map" +msgstr "mappa nomi utenti" + +#: param/loadparm.c:683 +msgid "password level" +msgstr "livello password" + +#: param/loadparm.c:684 +msgid "username level" +msgstr "livello nome utente" + +#: param/loadparm.c:685 +msgid "unix password sync" +msgstr "sincronizza password unix" + +#: param/loadparm.c:686 +msgid "restrict anonymous" +msgstr "limita anonimo" + +#: param/loadparm.c:687 +msgid "lanman auth" +msgstr "" + +#: param/loadparm.c:688 +msgid "ntlm auth" +msgstr "" + +#: param/loadparm.c:689 +msgid "plaintext to smbpasswd" +msgstr "da plaintext a smbpasswd" + +#: param/loadparm.c:690 +msgid "use rhosts" +msgstr "usa rhosts" + +#: param/loadparm.c:692 +msgid "username" +msgstr "nome utente" + +#: param/loadparm.c:693 +msgid "user" +msgstr "utente" + +#: param/loadparm.c:694 +msgid "users" +msgstr "utenti" + +#: param/loadparm.c:696 +msgid "guest account" +msgstr "account ospite" + +#: param/loadparm.c:697 +msgid "invalid users" +msgstr "utenti non validi" + +#: param/loadparm.c:698 +msgid "valid users" +msgstr "utenti validi" + +#: param/loadparm.c:699 +msgid "admin users" +msgstr "utenti amministratori" + +#: param/loadparm.c:700 +msgid "read list" +msgstr "" + +#: param/loadparm.c:701 +msgid "write list" +msgstr "" + +#: param/loadparm.c:702 +msgid "printer admin" +msgstr "amministratore stmapnati" + +#: param/loadparm.c:703 +msgid "force user" +msgstr "forza utente" + +#: param/loadparm.c:704 +msgid "force group" +msgstr "forza gruppo" + +#: param/loadparm.c:705 +msgid "group" +msgstr "gruppo" + +#: param/loadparm.c:707 +msgid "read only" +msgstr "sola lettura" + +#: param/loadparm.c:708 +msgid "write ok" +msgstr "ok scrittura" + +#: param/loadparm.c:709 +msgid "writeable" +msgstr "scrivibile" + +#: param/loadparm.c:710 +msgid "writable" +msgstr "scrivibile" + +#: param/loadparm.c:712 +msgid "create mask" +msgstr "" + +#: param/loadparm.c:713 +msgid "create mode" +msgstr "" + +#: param/loadparm.c:714 +msgid "force create mode" +msgstr "forza create mode" + +#: param/loadparm.c:715 +msgid "security mask" +msgstr "" + +#: param/loadparm.c:716 +msgid "force security mode" +msgstr "forza security mode" + +#: param/loadparm.c:717 +msgid "directory mask" +msgstr "" + +#: param/loadparm.c:718 +msgid "directory mode" +msgstr "" + +#: param/loadparm.c:719 +msgid "force directory mode" +msgstr "forza directory mode" + +#: param/loadparm.c:720 +msgid "directory security mask" +msgstr "" + +#: param/loadparm.c:721 +msgid "force directory security mode" +msgstr "forza directory security mode" + +#: param/loadparm.c:722 +msgid "inherit permissions" +msgstr "eredita permessi" + +#: param/loadparm.c:723 +msgid "guest only" +msgstr "solo ospite" + +#: param/loadparm.c:724 +msgid "only guest" +msgstr "solo ospite" + +#: param/loadparm.c:726 +msgid "guest ok" +msgstr "ok ospite" + +#: param/loadparm.c:727 +msgid "public" +msgstr "pubblico" + +#: param/loadparm.c:729 +msgid "only user" +msgstr "solo utente" + +#: param/loadparm.c:730 +msgid "hosts allow" +msgstr "permetti hosts" + +#: param/loadparm.c:731 +msgid "allow hosts" +msgstr "permetti hosts" + +#: param/loadparm.c:732 +msgid "hosts deny" +msgstr "vieta hosts" + +#: param/loadparm.c:733 +msgid "deny hosts" +msgstr "vieta hosts" + +#: param/loadparm.c:736 +msgid "Secure Socket Layer Options" +msgstr "Opzioni Secure Socket Layer" + +#: param/loadparm.c:737 +msgid "ssl" +msgstr "" + +#: param/loadparm.c:739 +msgid "ssl hosts" +msgstr "" + +#: param/loadparm.c:740 +msgid "ssl hosts resign" +msgstr "" + +#: param/loadparm.c:741 +msgid "ssl CA certDir" +msgstr "" + +#: param/loadparm.c:742 +msgid "ssl CA certFile" +msgstr "" + +#: param/loadparm.c:743 +msgid "ssl server cert" +msgstr "" + +#: param/loadparm.c:744 +msgid "ssl server key" +msgstr "" + +#: param/loadparm.c:745 +msgid "ssl client cert" +msgstr "" + +#: param/loadparm.c:746 +msgid "ssl client key" +msgstr "" + +#: param/loadparm.c:747 +msgid "ssl require clientcert" +msgstr "ssl richiede certificato client" + +#: param/loadparm.c:748 +msgid "ssl require servercert" +msgstr "ssl richiede certificato server" + +#: param/loadparm.c:749 +msgid "ssl ciphers" +msgstr "" + +#: param/loadparm.c:750 +msgid "ssl version" +msgstr "ssl versione" + +#: param/loadparm.c:751 +msgid "ssl compatibility" +msgstr "" + +#: param/loadparm.c:754 +msgid "Logging Options" +msgstr "Opzioni di Log" + +#: param/loadparm.c:755 +msgid "log level" +msgstr "livello log" + +#: param/loadparm.c:756 +msgid "debuglevel" +msgstr "livello debug" + +#: param/loadparm.c:757 +msgid "syslog" +msgstr "" + +#: param/loadparm.c:758 +msgid "syslog only" +msgstr "solo syslog" + +#: param/loadparm.c:759 +msgid "log file" +msgstr "file di log" + +#: param/loadparm.c:761 +msgid "max log size" +msgstr "massima dimensione log" + +#: param/loadparm.c:762 +msgid "timestamp logs" +msgstr "" + +#: param/loadparm.c:763 +msgid "debug timestamp" +msgstr "" + +#: param/loadparm.c:764 +msgid "debug hires timestamp" +msgstr "" + +#: param/loadparm.c:765 +msgid "debug pid" +msgstr "" + +#: param/loadparm.c:766 +msgid "debug uid" +msgstr "" + +#: param/loadparm.c:768 +msgid "Protocol Options" +msgstr "Opzioni Protocollo" + +#: param/loadparm.c:770 +msgid "protocol" +msgstr "protocollo" + +#: param/loadparm.c:771 +msgid "large readwrite" +msgstr "" + +#: param/loadparm.c:772 +msgid "max protocol" +msgstr "protocollo massimo" + +#: param/loadparm.c:773 +msgid "min protocol" +msgstr "protocollo minimo" + +#: param/loadparm.c:774 +msgid "unicode" +msgstr "" + +#: param/loadparm.c:775 +msgid "read bmpx" +msgstr "" + +#: param/loadparm.c:776 +msgid "read raw" +msgstr "" + +#: param/loadparm.c:777 +msgid "write raw" +msgstr "" + +#: param/loadparm.c:779 +msgid "nt smb support" +msgstr "supporto smb nt" + +#: param/loadparm.c:780 +msgid "nt pipe support" +msgstr "supporto pipe nt" + +#: param/loadparm.c:781 +msgid "nt acl support" +msgstr "supporto acl nt" + +#: param/loadparm.c:782 +msgid "announce version" +msgstr "annuncia versione" + +#: param/loadparm.c:783 +msgid "announce as" +msgstr "annuncia come" + +#: param/loadparm.c:784 +msgid "max mux" +msgstr "mux massimo" + +#: param/loadparm.c:785 +msgid "max xmit" +msgstr "xmit massimo" + +#: param/loadparm.c:787 +msgid "name resolve order" +msgstr "ordine risoluzione nomi" + +#: param/loadparm.c:788 +msgid "max packet" +msgstr "pacchetto massimo" + +#: param/loadparm.c:789 +msgid "packet size" +msgstr "dimensione pacchetto" + +#: param/loadparm.c:790 +msgid "max ttl" +msgstr "ttl massimo" + +#: param/loadparm.c:791 +msgid "max wins ttl" +msgstr "wins ttl massimo" + +#: param/loadparm.c:792 +msgid "min wins ttl" +msgstr "wins ttl minimo" + +#: param/loadparm.c:793 +msgid "time server" +msgstr "" + +#: param/loadparm.c:795 +msgid "Tuning Options" +msgstr "Opzioni Tuning" + +#: param/loadparm.c:797 +msgid "change notify timeout" +msgstr "" + +#: param/loadparm.c:798 +msgid "deadtime" +msgstr "" + +#: param/loadparm.c:799 +msgid "getwd cache" +msgstr "" + +#: param/loadparm.c:800 +msgid "keepalive" +msgstr "" + +#: param/loadparm.c:802 +msgid "lpq cache time" +msgstr "" + +#: param/loadparm.c:803 +msgid "max smbd processes" +msgstr "massimo n. processi smbd" + +#: param/loadparm.c:804 +msgid "max connections" +msgstr "massimo n. connessioni" + +#: param/loadparm.c:805 +msgid "paranoid server security" +msgstr "" + +#: param/loadparm.c:806 +msgid "max disk size" +msgstr "massima dimensione disco" + +#: param/loadparm.c:807 +msgid "max open files" +msgstr "massimo n. file aperti" + +#: param/loadparm.c:808 +msgid "min print space" +msgstr "spazio stampa minimo" + +#: param/loadparm.c:809 +msgid "read size" +msgstr "" + +#: param/loadparm.c:811 +msgid "socket options" +msgstr "opzioni socket" + +#: param/loadparm.c:812 +msgid "stat cache size" +msgstr "dimensione stat cache" + +#: param/loadparm.c:813 +msgid "strict allocate" +msgstr "allocazione stretta" + +#: param/loadparm.c:814 +msgid "strict sync" +msgstr "sincronizzazione stretta" + +#: param/loadparm.c:815 +msgid "sync always" +msgstr "sincronizza sempre" + +#: param/loadparm.c:816 +msgid "use mmap" +msgstr "usa mmap" + +#: param/loadparm.c:817 +msgid "hostname lookups" +msgstr "" + +#: param/loadparm.c:818 +msgid "write cache size" +msgstr "dimensione write cache" + +#: param/loadparm.c:820 +msgid "Printing Options" +msgstr "Opzioni di Stampa" + +#: param/loadparm.c:822 +msgid "total print jobs" +msgstr "job stampa totali" + +#: param/loadparm.c:823 +msgid "max print jobs" +msgstr "massimo n. job stampa" + +#: param/loadparm.c:824 +msgid "load printers" +msgstr "carica stampanti" + +#: param/loadparm.c:825 +msgid "printcap name" +msgstr "nome printcap" + +#: param/loadparm.c:826 +msgid "printcap" +msgstr "" + +#: param/loadparm.c:827 +msgid "printable" +msgstr "stmpabile" + +#: param/loadparm.c:828 +msgid "print ok" +msgstr "ok stampa" + +#: param/loadparm.c:829 +msgid "postscript" +msgstr "" + +#: param/loadparm.c:830 +msgid "printing" +msgstr "stampa" + +#: param/loadparm.c:831 +msgid "print command" +msgstr "comando stampa" + +#: param/loadparm.c:832 +msgid "disable spoolss" +msgstr "disabilita spoolss" + +#: param/loadparm.c:833 +msgid "lpq command" +msgstr "comando lpq" + +#: param/loadparm.c:834 +msgid "lprm command" +msgstr "comando lprm" + +#: param/loadparm.c:835 +msgid "lppause command" +msgstr "comando lppause" + +#: param/loadparm.c:836 +msgid "lpresume command" +msgstr "comando lpresume" + +#: param/loadparm.c:837 +msgid "queuepause command" +msgstr "comando queuepause" + +#: param/loadparm.c:838 +msgid "queueresume command" +msgstr "comando queueresume" + +#: param/loadparm.c:840 +msgid "enumports command" +msgstr "cmando enumports" + +#: param/loadparm.c:841 +msgid "addprinter command" +msgstr "comando addprinter" + +#: param/loadparm.c:842 +msgid "deleteprinter command" +msgstr "comando deleteprinter" + +#: param/loadparm.c:843 +msgid "show add printer wizard" +msgstr "mostra wizard aggiungi stampanti" + +#: param/loadparm.c:844 +msgid "os2 driver map" +msgstr "mappa driver os2" + +#: param/loadparm.c:846 +msgid "printer name" +msgstr "nome stampante" + +#: param/loadparm.c:847 +msgid "printer" +msgstr "stampante" + +#: param/loadparm.c:848 +msgid "use client driver" +msgstr "usa client driver" + +#: param/loadparm.c:849 +msgid "printer driver" +msgstr "driver stampante" + +#: param/loadparm.c:850 +msgid "printer driver file" +msgstr "file driver stampante" + +#: param/loadparm.c:851 +msgid "printer driver location" +msgstr "posizione driver stampante" + +#: param/loadparm.c:853 +msgid "Filename Handling" +msgstr "Gestione Nomi File" + +#: param/loadparm.c:854 +msgid "strip dot" +msgstr "togli il punto" + +#: param/loadparm.c:856 +msgid "mangled stack" +msgstr "" + +#: param/loadparm.c:857 +msgid "default case" +msgstr "" + +#: param/loadparm.c:858 +msgid "case sensitive" +msgstr "" + +#: param/loadparm.c:859 +msgid "casesignames" +msgstr "" + +#: param/loadparm.c:860 +msgid "preserve case" +msgstr "" + +#: param/loadparm.c:861 +msgid "short preserve case" +msgstr "" + +#: param/loadparm.c:862 +msgid "mangle case" +msgstr "" + +#: param/loadparm.c:863 +msgid "mangling char" +msgstr "" + +#: param/loadparm.c:864 +msgid "hide dot files" +msgstr "" + +#: param/loadparm.c:865 +msgid "hide unreadable" +msgstr "" + +#: param/loadparm.c:866 +msgid "delete veto files" +msgstr "" + +#: param/loadparm.c:867 +msgid "veto files" +msgstr "" + +#: param/loadparm.c:868 +msgid "hide files" +msgstr "nascondi file" + +#: param/loadparm.c:869 +msgid "veto oplock files" +msgstr "" + +#: param/loadparm.c:870 +msgid "map system" +msgstr "mappa system" + +#: param/loadparm.c:871 +msgid "map hidden" +msgstr "mappa hidden" + +#: param/loadparm.c:872 +msgid "map archive" +msgstr "mappa archive" + +#: param/loadparm.c:873 +msgid "mangled names" +msgstr "" + +#: param/loadparm.c:874 +msgid "mangled map" +msgstr "" + +#: param/loadparm.c:875 +msgid "stat cache" +msgstr "" + +#: param/loadparm.c:877 +msgid "Domain Options" +msgstr "Opzioni Dominio" + +#: param/loadparm.c:879 +msgid "domain admin group" +msgstr "gruppo amministratori dominio" + +#: param/loadparm.c:880 +msgid "domain guest group" +msgstr "gruppo ospiti dominio" + +#: param/loadparm.c:883 +msgid "groupname map" +msgstr "mappa nome gruppo" + +#: param/loadparm.c:886 +msgid "machine password timeout" +msgstr "" + +#: param/loadparm.c:888 +msgid "Logon Options" +msgstr "Opzioni di Logon" + +#: param/loadparm.c:890 +msgid "add user script" +msgstr "" + +#: param/loadparm.c:891 +msgid "delete user script" +msgstr "" + +#: param/loadparm.c:892 +msgid "add group script" +msgstr "" + +#: param/loadparm.c:893 +msgid "delete group script" +msgstr "" + +#: param/loadparm.c:894 +msgid "add user to group script" +msgstr "" + +#: param/loadparm.c:895 +msgid "delete user from group script" +msgstr "" + +#: param/loadparm.c:896 +msgid "add machine script" +msgstr "" + +#: param/loadparm.c:897 +msgid "shutdown script" +msgstr "" + +#: param/loadparm.c:898 +msgid "abort shutdown script" +msgstr "" + +#: param/loadparm.c:900 +msgid "logon script" +msgstr "" + +#: param/loadparm.c:901 +msgid "logon path" +msgstr "" + +#: param/loadparm.c:902 +msgid "logon drive" +msgstr "" + +#: param/loadparm.c:903 +msgid "logon home" +msgstr "" + +#: param/loadparm.c:904 +msgid "domain logons" +msgstr "" + +#: param/loadparm.c:906 +msgid "Browse Options" +msgstr "Opzioni Browsing" + +#: param/loadparm.c:908 +msgid "os level" +msgstr "livello os" + +#: param/loadparm.c:909 +msgid "lm announce" +msgstr "annuncio lm" + +#: param/loadparm.c:910 +msgid "lm interval" +msgstr "intervallo lm" + +#: param/loadparm.c:911 +msgid "preferred master" +msgstr "master preferito" + +#: param/loadparm.c:912 +msgid "prefered master" +msgstr "master preferito" + +#: param/loadparm.c:913 +msgid "local master" +msgstr "master locale" + +#: param/loadparm.c:914 +msgid "domain master" +msgstr "master dominio" + +#: param/loadparm.c:915 +msgid "browse list" +msgstr "lista browsing" + +#: param/loadparm.c:916 +msgid "browseable" +msgstr "" + +#: param/loadparm.c:917 +msgid "browsable" +msgstr "" + +#: param/loadparm.c:918 +msgid "enhanced browsing" +msgstr "" + +#: param/loadparm.c:920 +msgid "WINS Options" +msgstr "opzioni WINS" + +#: param/loadparm.c:921 +msgid "dns proxy" +msgstr "" + +#: param/loadparm.c:922 +msgid "wins proxy" +msgstr "" + +#: param/loadparm.c:924 +msgid "wins server" +msgstr "" + +#: param/loadparm.c:925 +msgid "wins support" +msgstr "" + +#: param/loadparm.c:926 +msgid "wins hook" +msgstr "" + +#: param/loadparm.c:928 +msgid "Locking Options" +msgstr "Opzioni Locking" + +#: param/loadparm.c:930 +msgid "blocking locks" +msgstr "" + +#: param/loadparm.c:931 +msgid "fake oplocks" +msgstr "" + +#: param/loadparm.c:932 +msgid "kernel oplocks" +msgstr "" + +#: param/loadparm.c:933 +msgid "locking" +msgstr "" + +#: param/loadparm.c:935 +msgid "oplocks" +msgstr "" + +#: param/loadparm.c:936 +msgid "level2 oplocks" +msgstr "" + +#: param/loadparm.c:937 +msgid "oplock break wait time" +msgstr "" + +#: param/loadparm.c:938 +msgid "oplock contention limit" +msgstr "" + +#: param/loadparm.c:939 +msgid "posix locking" +msgstr "" + +#: param/loadparm.c:940 +msgid "strict locking" +msgstr "" + +#: param/loadparm.c:941 +msgid "share modes" +msgstr "" + +#: param/loadparm.c:944 +msgid "Ldap Options" +msgstr "Opzioni Ldap" + +#: param/loadparm.c:946 +msgid "ldap server" +msgstr "" + +#: param/loadparm.c:947 +msgid "ldap port" +msgstr "" + +#: param/loadparm.c:948 +msgid "ldap suffix" +msgstr "" + +#: param/loadparm.c:949 +msgid "ldap filter" +msgstr "" + +#: param/loadparm.c:950 +msgid "ldap root" +msgstr "" + +#: param/loadparm.c:951 +msgid "ldap root passwd" +msgstr "" + +#: param/loadparm.c:954 +msgid "Miscellaneous Options" +msgstr "Opzioni Generiche" + +#: param/loadparm.c:955 +msgid "add share command" +msgstr "" + +#: param/loadparm.c:956 +msgid "change share command" +msgstr "" + +#: param/loadparm.c:957 +msgid "delete share command" +msgstr "" + +#: param/loadparm.c:959 +msgid "config file" +msgstr "file configurazione" + +#: param/loadparm.c:960 +msgid "preload" +msgstr "precarica" + +#: param/loadparm.c:961 +msgid "auto services" +msgstr "" + +#: param/loadparm.c:962 +msgid "lock dir" +msgstr "" + +#: param/loadparm.c:963 +msgid "lock directory" +msgstr "" + +#: param/loadparm.c:965 +msgid "utmp directory" +msgstr "" + +#: param/loadparm.c:966 +msgid "wtmp directory" +msgstr "" + +#: param/loadparm.c:967 +msgid "utmp" +msgstr "" + +#: param/loadparm.c:970 +msgid "default service" +msgstr "" + +#: param/loadparm.c:971 +msgid "default" +msgstr "" + +#: param/loadparm.c:972 +msgid "message command" +msgstr "comando message" + +#: param/loadparm.c:973 +msgid "dfree command" +msgstr "comando dfree" + +#: param/loadparm.c:974 +msgid "remote announce" +msgstr "annuncio remoto" + +#: param/loadparm.c:975 +msgid "remote browse sync" +msgstr "" + +#: param/loadparm.c:976 +msgid "socket address" +msgstr "" + +#: param/loadparm.c:977 +msgid "homedir map" +msgstr "" + +#: param/loadparm.c:978 +msgid "time offset" +msgstr "" + +#: param/loadparm.c:979 +msgid "NIS homedir" +msgstr "" + +#: param/loadparm.c:980 +msgid "-valid" +msgstr "" + +#: param/loadparm.c:982 +msgid "copy" +msgstr "copia" + +#: param/loadparm.c:983 +msgid "include" +msgstr "includi" + +#: param/loadparm.c:984 +msgid "exec" +msgstr "esegui" + +#: param/loadparm.c:985 +msgid "preexec" +msgstr "pre-esegui" + +#: param/loadparm.c:987 +msgid "preexec close" +msgstr "pre-esegui e chiudi" + +#: param/loadparm.c:988 +msgid "postexec" +msgstr "post-esegui" + +#: param/loadparm.c:989 +msgid "root preexec" +msgstr "root pre-esegui" + +#: param/loadparm.c:990 +msgid "root preexec close" +msgstr "root pre-esegui e chiudi" + +#: param/loadparm.c:991 +msgid "root postexec" +msgstr "root post-esegui" + +#: param/loadparm.c:992 +msgid "available" +msgstr "disponibile" + +#: param/loadparm.c:993 +msgid "volume" +msgstr "" + +#: param/loadparm.c:994 +msgid "fstype" +msgstr "tipo file system" + +#: param/loadparm.c:995 +msgid "set directory" +msgstr "" + +#: param/loadparm.c:996 +msgid "source environment" +msgstr "" + +#: param/loadparm.c:997 +msgid "wide links" +msgstr "" + +#: param/loadparm.c:998 +msgid "follow symlinks" +msgstr "" + +#: param/loadparm.c:999 +msgid "dont descend" +msgstr "non discendere" + +#: param/loadparm.c:1000 +msgid "magic script" +msgstr "" + +#: param/loadparm.c:1001 +msgid "magic output" +msgstr "" + +#: param/loadparm.c:1002 +msgid "delete readonly" +msgstr "cancella sola-lettura" + +#: param/loadparm.c:1003 +msgid "dos filemode" +msgstr "" + +#: param/loadparm.c:1004 +msgid "dos filetimes" +msgstr "" + +#: param/loadparm.c:1005 +msgid "dos filetime resolution" +msgstr "" + +#: param/loadparm.c:1007 +msgid "fake directory create times" +msgstr "" + +#: param/loadparm.c:1008 +msgid "panic action" +msgstr "" + +#: param/loadparm.c:1009 +msgid "hide local users" +msgstr "nascondi utenti locali" + +#: param/loadparm.c:1012 +msgid "VFS options" +msgstr "Opzioni VFS" + +#: param/loadparm.c:1014 +msgid "vfs object" +msgstr "" + +#: param/loadparm.c:1015 +msgid "vfs options" +msgstr "" + +#: param/loadparm.c:1018 +msgid "msdfs root" +msgstr "" + +#: param/loadparm.c:1019 +msgid "host msdfs" +msgstr "" + +#: param/loadparm.c:1021 +msgid "Winbind options" +msgstr "Opzioni Winbind" + +#: param/loadparm.c:1023 +msgid "winbind uid" +msgstr "" + +#: param/loadparm.c:1024 +msgid "winbind gid" +msgstr "" + +#: param/loadparm.c:1025 +msgid "template homedir" +msgstr "" + +#: param/loadparm.c:1026 +msgid "template shell" +msgstr "" + +#: param/loadparm.c:1027 +msgid "winbind separator" +msgstr "" + +#: param/loadparm.c:1028 +msgid "winbind cache time" +msgstr "" + +#: param/loadparm.c:1029 +msgid "winbind enum users" +msgstr "" + +#: param/loadparm.c:1030 +msgid "winbind enum groups" +msgstr "" diff --git a/source4/po/ja.msg b/source4/po/ja.msg new file mode 100644 index 0000000000..e77f34e3c4 --- /dev/null +++ b/source4/po/ja.msg @@ -0,0 +1,1821 @@ +# Japanese messages for international release of SWAT. +# Copyright (C) 2001 Ryo Kawahara , 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. +# +msgid "" +msgstr "" +"Project-Id-Version: i18n-swatE VERSION\n" +"POT-Creation-Date: 2001-09-20 20:29+0900\n" +"PO-Revision-Date: 2000-04-03 17:55+09:00\n" +"Last-Translator: TAKAHASHI Motonobu \n" +"Language-Team: Samba Team \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=Shift_JIS\n" +"Content-Transfer-Encoding: \n" + +#: web/swat.c:120 +#, c-format +msgid "ERROR: Can't open %s\n" +msgstr "%s ‚ƒI[ƒvƒ“‚Ċ‚Ğ‚Ü‚ı‚ñ\n" + +#. +#. str = stripspace(parm->label); +#. strlower (str); //monyo +#. d_printf("%s       %s", +#. str, _("Help"), parm->label); +#. +#: web/swat.c:211 +msgid "Help" +msgstr "à–" + +#: web/swat.c:217 web/swat.c:231 web/swat.c:246 web/swat.c:254 web/swat.c:263 +#: web/swat.c:272 web/swat.c:278 web/swat.c:284 web/swat.c:297 +msgid "Set Default" +msgstr "Šù’è’l‚É–ß‚·" + +#: web/swat.c:502 +#, c-format +msgid "Logged in as %s

\n" +msgstr "%s‚Ĉ‚µ‚ăƒOƒCƒ“

\n" + +#: web/swat.c:505 +msgid "Home" +msgstr "ƒz[ƒ€" + +#: web/swat.c:507 +msgid "Globals" +msgstr "‘S‘̐Ŭ’è" + +#: web/swat.c:508 +msgid "Shares" +msgstr "‹¤—LŬ’è" + +#: web/swat.c:509 +msgid "Printers" +msgstr "ƒvƒŠƒ“ƒ^Ŭ’è" + +#: web/swat.c:512 +msgid "Status" +msgstr "“ìó‹µ" + +#: web/swat.c:513 +msgid "View Config" +msgstr "Ŭ’è•\\ŽĤ" + +#: web/swat.c:515 +msgid "Password Management" +msgstr "ƒpƒXƒ[ƒhŠÇ—Eƒ†[ƒUŠÇ—" + +#: web/swat.c:539 +msgid "Current Config" +msgstr "ŒğŬ‚̐Ŭ’è" + +#: web/swat.c:543 +msgid "Normal View" +msgstr "•W€•\ŽĤ" + +#: web/swat.c:545 +msgid "Full View" +msgstr "Š‘S•\ŽĤ" + +#: web/swat.c:561 +msgid "Global Variables" +msgstr "‘S‘̐Ŭ’è" + +#: web/swat.c:575 web/swat.c:671 web/swat.c:1014 +msgid "Commit Changes" +msgstr "Ŭ’è•ÏX" + +#: web/swat.c:579 web/swat.c:674 web/swat.c:1016 +msgid "Reset Values" +msgstr "ƒŠƒZƒbƒg" + +#: web/swat.c:581 web/swat.c:676 web/swat.c:1018 +msgid "Advanced View" +msgstr "Ú×•\ŽĤ" + +#: web/swat.c:583 web/swat.c:678 web/swat.c:1020 +msgid "Basic View" +msgstr "•W€•\ŽĤ" + +#: web/swat.c:613 +msgid "Share Parameters" +msgstr "‹¤—LŬ’è" + +#: web/swat.c:642 +msgid "Choose Share" +msgstr "‹¤—L‘I‘" + +#: web/swat.c:656 +msgid "Delete Share" +msgstr "‹¤—Líœ" + +#: web/swat.c:663 +msgid "Create Share" +msgstr "V‹K‹¤—LìĴ" + +#: web/swat.c:708 +msgid "password change in demo mode rejected\n" +msgstr "ƒfƒ‚Eƒ‚[ƒh‚Ċ‚̃pƒXƒ[ƒh•ÏX‚Í‚Ċ‚Ğ‚Ü‚ı‚ñ\n" + +#: web/swat.c:747 +msgid " Must specify \"User Name\" \n" +msgstr "uƒ†[ƒU–ĵv‚“ü—Í‚­‚‚³‚˘\n" + +#: web/swat.c:763 +msgid " Must specify \"Old Password\" \n" +msgstr "u‹ŒƒpƒXƒ[ƒhv‚“ü—Í‚µ‚Ä‚­‚‚³‚˘\n" + +#: web/swat.c:769 +msgid " Must specify \"Remote Machine\" \n" +msgstr "uƒŠƒ‚[ƒg ƒ}ƒVƒ“v‚“ü—Í‚µ‚Ä‚­‚‚³‚˘\n" + +#: web/swat.c:776 +msgid " Must specify \"New, and Re-typed Passwords\" \n" +msgstr "uVƒpƒXƒ[ƒhv‚“ü—Í‚µAÄ“ü—Í‚à‚µ‚Ä‚­‚‚³‚˘\n" + +#: web/swat.c:782 +msgid " Re-typed password didn't match new password\n" +msgstr "VƒpƒXƒ[ƒh‚̍ēü—Í‚ŞŠÔˆá‚Á‚Ä‚˘‚Ü‚·\n" + +#: web/swat.c:812 +#, c-format +msgid " The passwd for '%s' has been changed. \n" +msgstr " '%s' ‚̃pƒXƒ[ƒh‚͕ύX‚³‚ê‚Ü‚µ‚½ \n" + +#: web/swat.c:814 +#, c-format +msgid " The passwd for '%s' has NOT been changed. \n" +msgstr " '%s' ‚̃pƒXƒ[ƒh‚͕ύX‚³‚ê‚Ü‚ı‚ñ‚Ċ‚µ‚½ \n" + +#: web/swat.c:838 +msgid "Server Password Management" +msgstr "ƒ[ƒJƒ‹ ƒ}ƒVƒ“‚̃pƒXƒ[ƒhŠÇ—" + +#. +#. * Create all the dialog boxes for data collection +#. +#: web/swat.c:847 web/swat.c:894 +msgid " User Name : " +msgstr "ƒ†[ƒU–ĵ : " + +#: web/swat.c:850 web/swat.c:896 +msgid " Old Password : " +msgstr "‹ŒƒpƒXƒ[ƒh : " + +#: web/swat.c:853 web/swat.c:898 +msgid " New Password : " +msgstr "VƒpƒXƒ[ƒh : " + +#: web/swat.c:855 web/swat.c:900 +msgid " Re-type New Password : " +msgstr "VƒpƒXƒ[ƒhÄ“ü—Í : " + +#: web/swat.c:863 web/swat.c:911 +msgid "Change Password" +msgstr "ƒpƒXƒ[ƒh•ÏX" + +#: web/swat.c:866 +msgid "Add New User" +msgstr "V‹Kƒ†[ƒU’ljÁ" + +#: web/swat.c:868 +msgid "Delete User" +msgstr "ƒ†[ƒU‚íœ‚·‚é" + +#: web/swat.c:870 +msgid "Disable User" +msgstr "Žg—p•s‰Â‚É‚·‚é" + +#: web/swat.c:872 +msgid "Enable User" +msgstr "Žg—p‰Â”\‚É‚·‚é" + +#: web/swat.c:885 +msgid "Client/Server Password Management" +msgstr "ƒŠƒ‚[ƒg ƒ}ƒVƒ“‚̃pƒXƒ[ƒhŠÇ—" + +#: web/swat.c:902 +msgid " Remote Machine : " +msgstr " ƒŠƒ‚[ƒg ƒ}ƒVƒ“ : " + +#: web/swat.c:940 +msgid "Printer Parameters" +msgstr "ƒvƒŠƒ“ƒ^Ŭ’è [Printer]" + +#: web/swat.c:942 +msgid "Important Note:" +msgstr "*’:" + +#: web/swat.c:943 +msgid "Printer names marked with [*] in the Choose Printer drop-down box " +msgstr "–ĵ‘O‚̐ĉ“Ş‚É [*] ‚ނ‚˘‚½ƒvƒŠƒ“ƒ^" + +#: web/swat.c:944 +msgid "are autoloaded printers from " +msgstr "‚́A" + +#: web/swat.c:945 +msgid "Printcap Name" +msgstr "printcap name ƒpƒ‰ƒ[ƒ^" + +#: web/swat.c:946 +msgid "Attempting to delete these printers from SWAT will have no effect.\n" +msgstr "‚İ‚çŽİ“Ŭ’肳‚ꂽ‚à‚Ì‚Ċ‚·‚İ‚çAíœ‚·‚é‚ħ‚Ĉ‚Í‚Ċ‚Ğ‚Ü‚ı‚ñB\n" + +#: web/swat.c:980 +msgid "Choose Printer" +msgstr "ƒvƒŠƒ“ƒ^‘I‘" + +#: web/swat.c:999 +msgid "Delete Printer" +msgstr "ƒvƒŠƒ“ƒ^íœ" + +#: web/swat.c:1006 +msgid "Create Printer" +msgstr "ƒvƒŠƒ“ƒ^V‹KìĴ" + +#: web/statuspage.c:40 +msgid "DENY_NONE" +msgstr "‹‘”Û‚È‚µ" + +#: web/statuspage.c:41 +msgid "DENY_ALL " +msgstr "‚·‚ׂċ‘”Û" + +#: web/statuspage.c:42 +msgid "DENY_DOS " +msgstr "DOS‚‹‘”Û" + +#: web/statuspage.c:43 +msgid "DENY_READ " +msgstr "ŽQĈ‚‹‘”Û" + +#: web/statuspage.c:44 +msgid "DENY_WRITE " +msgstr "XV‚‹‘”Û" + +#: web/statuspage.c:50 +msgid "RDONLY " +msgstr "ŽQĈ‚Ì‚Ŭ" + +#: web/statuspage.c:51 +msgid "WRONLY " +msgstr "‘}“ü‚Ì‚Ŭ" + +#: web/statuspage.c:52 +msgid "RDWR " +msgstr "XV " + +#: web/statuspage.c:60 +msgid "EXCLUSIVE+BATCH " +msgstr "ê—L+ƒoƒbƒ` " + +#: web/statuspage.c:62 +msgid "EXCLUSIVE " +msgstr "ê—L " + +#: web/statuspage.c:64 +msgid "BATCH " +msgstr "ƒoƒbƒ` " + +#: web/statuspage.c:66 +msgid "LEVEL_II " +msgstr "ƒŒƒxƒ‹_II " + +#: web/statuspage.c:68 +msgid "NONE " +msgstr "‚È‚µ " + +#: web/statuspage.c:195 +msgid "Server Status" +msgstr "ƒT[ƒo[“ìó‹µ" + +#: web/statuspage.c:200 +msgid "Auto Refresh" +msgstr "Žİ“Ä•\ŽĤ" + +#: web/statuspage.c:201 web/statuspage.c:206 +msgid "Refresh Interval: " +msgstr "Ä•\ŽĤŠÔŠu(•b): " + +#: web/statuspage.c:205 +msgid "Stop Refreshing" +msgstr "Žİ“•\ŽĤ’âŽ~" + +#: web/statuspage.c:220 +msgid "version:" +msgstr "ƒo[ƒWƒ‡ƒ“:" + +#: web/statuspage.c:223 +msgid "smbd:" +msgstr "ƒtƒ@ƒCƒ‹‹¤—Lƒf[ƒ‚ƒ“(smbd):" + +#: web/statuspage.c:223 web/statuspage.c:235 +msgid "running" +msgstr "“ì’†" + +#: web/statuspage.c:223 web/statuspage.c:235 +msgid "not running" +msgstr "’âŽ~’†" + +#: web/statuspage.c:226 +msgid "Stop smbd" +msgstr "smbd’âŽ~" + +#: web/statuspage.c:228 +msgid "Start smbd" +msgstr "smbd‹N“" + +#: web/statuspage.c:230 +msgid "Restart smbd" +msgstr "smbdÄ‹N“" + +#: web/statuspage.c:235 +msgid "nmbd:" +msgstr "ƒl[ƒ€ ƒT[ƒrƒX ƒf[ƒ‚ƒ“(nmbd)" + +#: web/statuspage.c:238 +msgid "Stop nmbd" +msgstr "nmbd’âŽ~" + +#: web/statuspage.c:240 +msgid "Start nmbd" +msgstr "nmbd‹N“" + +#: web/statuspage.c:242 +msgid "Restart nmbd" +msgstr "nmbdÄ‹N“" + +#: web/statuspage.c:249 +msgid "Active Connections" +msgstr "Ú‘ħ’†ƒNƒ‰ƒCƒAƒ“ƒg" + +#: web/statuspage.c:251 web/statuspage.c:264 web/statuspage.c:272 +msgid "PID" +msgstr "ƒvƒƒZƒXID" + +#: web/statuspage.c:251 web/statuspage.c:264 +msgid "Client" +msgstr "ƒNƒ‰ƒCƒAƒ“ƒg" + +#: web/statuspage.c:251 +msgid "IP address" +msgstr "IPƒAƒhƒŒƒX" + +#: web/statuspage.c:251 web/statuspage.c:264 web/statuspage.c:272 +msgid "Date" +msgstr "“ú•t" + +#: web/statuspage.c:253 +msgid "Kill" +msgstr "Ĝ’f" + +#: web/statuspage.c:261 +msgid "Active Shares" +msgstr "Ú‘ħ’†‹¤—L" + +#: web/statuspage.c:264 +msgid "Share" +msgstr "‹¤—L–ĵ" + +#: web/statuspage.c:264 +msgid "User" +msgstr "ƒ†[ƒU" + +#: web/statuspage.c:264 +msgid "Group" +msgstr "ƒOƒ‹[ƒv" + +#: web/statuspage.c:270 +msgid "Open Files" +msgstr "Žg—p’†ƒtƒ@ƒCƒ‹" + +#: web/statuspage.c:272 +msgid "Sharing" +msgstr "”r‘ĵƒ‚[ƒh" + +#: web/statuspage.c:272 +msgid "R/W" +msgstr "ŽQĈ/XV" + +#: web/statuspage.c:272 +msgid "Oplock" +msgstr "•Ö‹XƒƒbƒN(Oplock)" + +#: web/statuspage.c:272 +msgid "File" +msgstr "ƒtƒ@ƒCƒ‹–ĵ" + +#: param/loadparm.c:641 +msgid "Base Options" +msgstr "Šî–{ƒIƒvƒVƒ‡ƒ“" + +#: param/loadparm.c:643 +#, fuzzy +msgid "dos charset" +msgstr "—LŒĝ‚È•ĥŽš" + +#: param/loadparm.c:644 +#, fuzzy +msgid "unix charset" +msgstr "—LŒĝ‚È•ĥŽš" + +#: param/loadparm.c:645 +msgid "display charset" +msgstr "" + +#: param/loadparm.c:646 +msgid "comment" +msgstr "ƒRƒƒ“ƒg" + +#: param/loadparm.c:647 +msgid "path" +msgstr "ƒpƒX" + +#: param/loadparm.c:648 +msgid "directory" +msgstr "ƒfƒBƒŒƒNƒgƒŠ" + +#: param/loadparm.c:649 +msgid "workgroup" +msgstr "ƒ[ƒNƒOƒ‹[ƒv" + +#: param/loadparm.c:650 +msgid "netbios name" +msgstr "netbios –ĵ" + +#: param/loadparm.c:651 +msgid "netbios aliases" +msgstr "netbios ƒGƒCƒŠƒAƒX" + +#: param/loadparm.c:652 +msgid "netbios scope" +msgstr "netbios ƒXƒR[ƒv" + +#: param/loadparm.c:653 +msgid "server string" +msgstr "ƒT[ƒo•ĥŽš—ñ" + +#: param/loadparm.c:654 +msgid "interfaces" +msgstr "ƒCƒ“ƒ^[ƒtƒF[ƒX" + +#: param/loadparm.c:655 +msgid "bind interfaces only" +msgstr "‚ħ‚̃Cƒ“ƒ^[ƒtƒF[ƒX‚Ì‚ŬŽg—p" + +#: param/loadparm.c:657 +msgid "Security Options" +msgstr "ƒZƒLƒ…ƒŠƒeƒB ƒIƒvƒVƒ‡ƒ“" + +#: param/loadparm.c:659 +msgid "security" +msgstr "ƒZƒLƒ…ƒŠƒeƒB" + +#: param/loadparm.c:660 +msgid "encrypt passwords" +msgstr "ƒpƒXƒ[ƒh‚ˆ†‰ğ" + +#: param/loadparm.c:661 +msgid "update encrypted" +msgstr "ˆ†‰ğƒpƒXƒ[ƒh‚ɍXV" + +#: param/loadparm.c:662 +msgid "allow trusted domains" +msgstr "M—Š‚Ċ‚Ğ‚éƒhƒƒCƒ“‚‹–‰Â" + +#: param/loadparm.c:663 +msgid "alternate permissions" +msgstr "‘sƒp[ƒ~ƒbƒVƒ‡ƒ“" + +#: param/loadparm.c:664 +msgid "hosts equiv" +msgstr "“Ż“™‚̃zƒXƒg" + +#: param/loadparm.c:665 +msgid "min passwd length" +msgstr "ĊĴƒpƒXƒ[ƒh’·" + +#: param/loadparm.c:666 +msgid "min password length" +msgstr "ĊĴƒpƒXƒ[ƒh’·" + +#: param/loadparm.c:667 +msgid "map to guest" +msgstr "ƒQƒXƒg‚Ƀ}ƒbƒv" + +#: param/loadparm.c:668 +msgid "null passwords" +msgstr "‹óƒpƒXƒ[ƒh" + +#: param/loadparm.c:669 +#, fuzzy +msgid "obey pam restrictions" +msgstr "ĉ“Ç‚Ŭ" + +#: param/loadparm.c:670 +msgid "password server" +msgstr "ƒpƒXƒ[ƒh ƒT[ƒo" + +#: param/loadparm.c:671 +msgid "smb passwd file" +msgstr "smb passwd ƒtƒ@ƒCƒ‹" + +#: param/loadparm.c:672 +#, fuzzy +msgid "private dir" +msgstr "ƒvƒŠƒ“ƒ^ ƒhƒ‰ƒCƒo" + +#: param/loadparm.c:673 +msgid "passdb module path" +msgstr "" + +#: param/loadparm.c:674 +msgid "root directory" +msgstr "ƒ‹[ƒg ƒfƒBƒŒƒNƒgƒŠ" + +#: param/loadparm.c:675 +msgid "root dir" +msgstr "ƒ‹[ƒg ƒfƒBƒŒƒNƒgƒŠ" + +#: param/loadparm.c:676 +msgid "root" +msgstr "ƒ‹[ƒg" + +#: param/loadparm.c:678 +#, fuzzy +msgid "pam password change" +msgstr "ĊĴƒpƒXƒ[ƒh’·" + +#: param/loadparm.c:679 +msgid "passwd program" +msgstr "ƒpƒXƒ[ƒh ƒvƒƒOƒ‰ƒ€" + +#: param/loadparm.c:680 +msgid "passwd chat" +msgstr "ƒpƒXƒ[ƒh ƒ`ƒƒƒbƒg" + +#: param/loadparm.c:681 +msgid "passwd chat debug" +msgstr "ƒpƒXƒ[ƒh ƒ`ƒƒƒbƒg ƒfƒoƒbƒO" + +#: param/loadparm.c:682 +msgid "username map" +msgstr "ƒ†[ƒU–ĵƒ}ƒbƒv" + +#: param/loadparm.c:683 +msgid "password level" +msgstr "ƒpƒXƒ[ƒh ƒŒƒxƒ‹" + +#: param/loadparm.c:684 +msgid "username level" +msgstr "ƒ†[ƒU–ĵƒŒƒxƒ‹" + +#: param/loadparm.c:685 +msgid "unix password sync" +msgstr "unix ƒpƒXƒ[ƒh‚“ŻŠú‚³‚ı‚é" + +#: param/loadparm.c:686 +msgid "restrict anonymous" +msgstr "“½–ĵƒAƒNƒZƒX‚̐§ŒÀ" + +#: param/loadparm.c:687 +msgid "lanman auth" +msgstr "" + +#: param/loadparm.c:688 +msgid "ntlm auth" +msgstr "" + +#: param/loadparm.c:689 +msgid "plaintext to smbpasswd" +msgstr "" + +#: param/loadparm.c:690 +msgid "use rhosts" +msgstr "rhosts ‚Žg‚¤" + +#: param/loadparm.c:692 +msgid "username" +msgstr "ƒ†[ƒU–ĵ" + +#: param/loadparm.c:693 +msgid "user" +msgstr "ƒ†[ƒU" + +#: param/loadparm.c:694 +msgid "users" +msgstr "ƒ†[ƒU" + +#: param/loadparm.c:696 +msgid "guest account" +msgstr "ƒQƒXƒg ƒAƒJƒEƒ“ƒg" + +#: param/loadparm.c:697 +msgid "invalid users" +msgstr "–³Œĝ‚ȃ†[ƒU" + +#: param/loadparm.c:698 +msgid "valid users" +msgstr "—LŒĝ‚ȃ†[ƒU" + +#: param/loadparm.c:699 +msgid "admin users" +msgstr "ŠÇ—ƒ†[ƒU" + +#: param/loadparm.c:700 +msgid "read list" +msgstr "“Ç‚ŬŽĉ‚胊ƒXƒg" + +#: param/loadparm.c:701 +msgid "write list" +msgstr "‘‚Ѝž‚ŬƒŠƒXƒg" + +#: param/loadparm.c:702 +#, fuzzy +msgid "printer admin" +msgstr "ƒvƒŠƒ“ƒ^–ĵ" + +#: param/loadparm.c:703 +msgid "force user" +msgstr "‹­§‚·‚郆[ƒU" + +#: param/loadparm.c:704 +msgid "force group" +msgstr "‹­§‚·‚éƒOƒ‹[ƒv" + +#: param/loadparm.c:705 +msgid "group" +msgstr "ƒOƒ‹[ƒv" + +#: param/loadparm.c:707 +msgid "read only" +msgstr "“Ç‚ŬŽĉ‚è‚Ì‚Ŭ" + +#: param/loadparm.c:708 +msgid "write ok" +msgstr "‘‚Ѝž‚Ŭ‰Â" + +#: param/loadparm.c:709 +msgid "writeable" +msgstr "‘‚Ѝž‚Ŭ‰Â" + +#: param/loadparm.c:710 +msgid "writable" +msgstr "‘‚Ѝž‚Ŭ‰Â" + +#: param/loadparm.c:712 +msgid "create mask" +msgstr "ìĴŽž‚Ƀ}ƒXƒN" + +#: param/loadparm.c:713 +msgid "create mode" +msgstr "ìĴŽž‚̃‚[ƒh" + +#: param/loadparm.c:714 +msgid "force create mode" +msgstr "‹­§‚·‚éìĴŽž‚̃‚[ƒh" + +#: param/loadparm.c:715 +msgid "security mask" +msgstr "ƒZƒLƒ…ƒŠƒeƒB ƒ}ƒXƒN" + +#: param/loadparm.c:716 +msgid "force security mode" +msgstr "‹­§‚·‚éƒZƒLƒ…ƒŠƒeƒB ƒ‚[ƒh" + +#: param/loadparm.c:717 +msgid "directory mask" +msgstr "ƒfƒBƒŒƒNƒgƒŠ ƒ}ƒXƒN" + +#: param/loadparm.c:718 +msgid "directory mode" +msgstr "ƒfƒBƒŒƒNƒgƒŠ ƒ‚[ƒh" + +#: param/loadparm.c:719 +msgid "force directory mode" +msgstr "‹­§‚·‚éƒfƒBƒŒƒNƒgƒŠ ƒ‚[ƒh" + +#: param/loadparm.c:720 +msgid "directory security mask" +msgstr "ƒfƒBƒŒƒNƒgƒŠ‚̃ZƒLƒ…ƒŠƒeƒB ƒ}ƒXƒN" + +#: param/loadparm.c:721 +msgid "force directory security mode" +msgstr "‹­§‚·‚éƒfƒBƒŒƒNƒgƒŠ‚̃ZƒLƒ…ƒŠƒeƒB ƒ‚[ƒh" + +#: param/loadparm.c:722 +msgid "inherit permissions" +msgstr "ƒp[ƒ~ƒbƒVƒ‡ƒ“‚Œp³" + +#: param/loadparm.c:723 +msgid "guest only" +msgstr "ƒQƒXƒg‚Ì‚Ŭ" + +#: param/loadparm.c:724 +msgid "only guest" +msgstr "ƒQƒXƒg‚Ì‚Ŭ" + +#: param/loadparm.c:726 +msgid "guest ok" +msgstr "ƒQƒXƒg‰Â" + +#: param/loadparm.c:727 +msgid "public" +msgstr "ƒpƒuƒŠƒbƒN" + +#: param/loadparm.c:729 +msgid "only user" +msgstr "ƒ†[ƒU‚Ì‚Ŭ" + +#: param/loadparm.c:730 +msgid "hosts allow" +msgstr "‹–‰Â‚·‚éƒzƒXƒg" + +#: param/loadparm.c:731 +msgid "allow hosts" +msgstr "‹–‰Â‚·‚éƒzƒXƒg" + +#: param/loadparm.c:732 +msgid "hosts deny" +msgstr "‹‘”Û‚·‚éƒzƒXƒg" + +#: param/loadparm.c:733 +msgid "deny hosts" +msgstr "‹‘”Û‚·‚éƒzƒXƒg" + +#: param/loadparm.c:736 +msgid "Secure Socket Layer Options" +msgstr "ƒZƒLƒ…ƒA ƒ\ƒPƒbƒg ƒŒƒCƒA[ ƒIƒvƒVƒ‡ƒ“" + +#: param/loadparm.c:737 +msgid "ssl" +msgstr "ssl" + +#: param/loadparm.c:739 +msgid "ssl hosts" +msgstr "ssl ƒzƒXƒg" + +#: param/loadparm.c:740 +msgid "ssl hosts resign" +msgstr "ssl –˘Žg—pƒzƒXƒg" + +#: param/loadparm.c:741 +msgid "ssl CA certDir" +msgstr "ssl CA ”FĜƒfƒBƒŒƒNƒgƒŠ" + +#: param/loadparm.c:742 +msgid "ssl CA certFile" +msgstr "ssl CA ”FĜƒtƒ@ƒCƒ‹" + +#: param/loadparm.c:743 +msgid "ssl server cert" +msgstr "ssl ƒT[ƒo”FĜ" + +#: param/loadparm.c:744 +msgid "ssl server key" +msgstr "ssl ƒT[ƒoŒ" + +#: param/loadparm.c:745 +msgid "ssl client cert" +msgstr "ssl ƒNƒ‰ƒCƒAƒ“ƒg”FĜ" + +#: param/loadparm.c:746 +msgid "ssl client key" +msgstr "ssl ƒNƒ‰ƒCƒAƒ“ƒgŒ" + +#: param/loadparm.c:747 +msgid "ssl require clientcert" +msgstr "ssl ƒNƒ‰ƒCƒAƒ“ƒg”FĜ‚—v‹" + +#: param/loadparm.c:748 +msgid "ssl require servercert" +msgstr "ssl ƒT[ƒo”FĜ‚—v‹" + +#: param/loadparm.c:749 +msgid "ssl ciphers" +msgstr "ssl ˆ†" + +#: param/loadparm.c:750 +msgid "ssl version" +msgstr "ssl ƒo[ƒWƒ‡ƒ“" + +#: param/loadparm.c:751 +msgid "ssl compatibility" +msgstr "ssl ŒŬŠ·Ğ" + +#: param/loadparm.c:754 +msgid "Logging Options" +msgstr "ƒƒMƒ“ƒO ƒIƒvƒVƒ‡ƒ“" + +#: param/loadparm.c:755 +msgid "log level" +msgstr "ƒƒO ƒŒƒxƒ‹" + +#: param/loadparm.c:756 +#, fuzzy +msgid "debuglevel" +msgstr "ƒfƒoƒbƒOƒŒƒxƒ‹" + +#: param/loadparm.c:757 +msgid "syslog" +msgstr "syslog" + +#: param/loadparm.c:758 +msgid "syslog only" +msgstr "syslog ‚Ì‚Ŭ" + +#: param/loadparm.c:759 +msgid "log file" +msgstr "ƒƒO ƒtƒ@ƒCƒ‹" + +#: param/loadparm.c:761 +msgid "max log size" +msgstr "Ċ‘ċƒƒO ƒTƒCƒY" + +#: param/loadparm.c:762 +msgid "timestamp logs" +msgstr "ƒ^ƒCƒ€ƒXƒ^ƒ“ƒv ƒƒO" + +#: param/loadparm.c:763 +msgid "debug timestamp" +msgstr "ƒfƒoƒbƒO ƒ^ƒCƒ€ƒXƒ^ƒ“ƒv" + +#: param/loadparm.c:764 +#, fuzzy +msgid "debug hires timestamp" +msgstr "ƒfƒoƒbƒO ƒ^ƒCƒ€ƒXƒ^ƒ“ƒv" + +#: param/loadparm.c:765 +msgid "debug pid" +msgstr "ƒfƒoƒbƒO pid" + +#: param/loadparm.c:766 +msgid "debug uid" +msgstr "ƒfƒoƒbƒO uid" + +#: param/loadparm.c:768 +msgid "Protocol Options" +msgstr "ƒvƒƒgƒRƒ‹ ƒIƒvƒVƒ‡ƒ“" + +#: param/loadparm.c:770 +msgid "protocol" +msgstr "ƒvƒƒgƒRƒ‹" + +#: param/loadparm.c:771 +msgid "large readwrite" +msgstr "" + +#: param/loadparm.c:772 +#, fuzzy +msgid "max protocol" +msgstr "ƒvƒƒgƒRƒ‹" + +#: param/loadparm.c:773 +#, fuzzy +msgid "min protocol" +msgstr "ƒvƒƒgƒRƒ‹" + +#: param/loadparm.c:774 +msgid "unicode" +msgstr "" + +#: param/loadparm.c:775 +msgid "read bmpx" +msgstr "bmpx “Ç‚Ŭo‚µ" + +#: param/loadparm.c:776 +msgid "read raw" +msgstr "raw “Ç‚Ŭo‚µ" + +#: param/loadparm.c:777 +msgid "write raw" +msgstr "raw ‘‚Ѝž‚Ŭ" + +#: param/loadparm.c:779 +msgid "nt smb support" +msgstr "nt smb ƒTƒ|[ƒg" + +#: param/loadparm.c:780 +msgid "nt pipe support" +msgstr "nt pipe ƒTƒ|[ƒg" + +#: param/loadparm.c:781 +msgid "nt acl support" +msgstr "nt acl ƒTƒ|[ƒg" + +#: param/loadparm.c:782 +msgid "announce version" +msgstr "ƒAƒiƒEƒ“ƒX ƒo[ƒWƒ‡ƒ“" + +#: param/loadparm.c:783 +msgid "announce as" +msgstr "ƒAƒiƒEƒ“ƒX‚·‚éŽí—Ŝ" + +#: param/loadparm.c:784 +msgid "max mux" +msgstr "Ċ‘ċ mux" + +#: param/loadparm.c:785 +msgid "max xmit" +msgstr "Ċ‘ċ xmit" + +#: param/loadparm.c:787 +msgid "name resolve order" +msgstr "–ĵ‘O‰Œˆ‚̏‡”Ô" + +#: param/loadparm.c:788 +msgid "max packet" +msgstr "Ċ‘ċƒpƒPƒbƒg" + +#: param/loadparm.c:789 +msgid "packet size" +msgstr "ƒpƒPƒbƒg ƒTƒCƒY" + +#: param/loadparm.c:790 +msgid "max ttl" +msgstr "Ċ‘ċ ttl" + +#: param/loadparm.c:791 +msgid "max wins ttl" +msgstr "Ċ‘ċ wins ttl" + +#: param/loadparm.c:792 +msgid "min wins ttl" +msgstr "ĊĴ wins ttl" + +#: param/loadparm.c:793 +msgid "time server" +msgstr "ƒ^ƒCƒ€ ƒT[ƒo" + +#: param/loadparm.c:795 +msgid "Tuning Options" +msgstr "ƒ`ƒ…[ƒjƒ“ƒO ƒIƒvƒVƒ‡ƒ“" + +#: param/loadparm.c:797 +msgid "change notify timeout" +msgstr "XV’Ê’m‚ÌŠÔŠu" + +#: param/loadparm.c:798 +msgid "deadtime" +msgstr "Ĝ’f‚Ü‚Ċ‚ÌŽžŠÔ" + +#: param/loadparm.c:799 +msgid "getwd cache" +msgstr "getwd ƒLƒƒƒbƒVƒ…" + +#: param/loadparm.c:800 +msgid "keepalive" +msgstr "" + +#: param/loadparm.c:802 +msgid "lpq cache time" +msgstr "lpq ƒLƒƒƒbƒVƒ…ŽžŠÔ" + +#: param/loadparm.c:803 +msgid "max smbd processes" +msgstr "" + +#: param/loadparm.c:804 +msgid "max connections" +msgstr "Ċ‘ċÚ‘ħ”" + +#: param/loadparm.c:805 +#, fuzzy +msgid "paranoid server security" +msgstr "ƒ†[ƒU’ljÁƒXƒNƒŠƒvƒg" + +#: param/loadparm.c:806 +msgid "max disk size" +msgstr "Ċ‘ċƒfƒBƒXƒN ƒTƒCƒY" + +#: param/loadparm.c:807 +msgid "max open files" +msgstr "Ċ‘ċƒtƒ@ƒCƒ‹ ƒI[ƒvƒ“”" + +#: param/loadparm.c:808 +msgid "min print space" +msgstr "ĊĴˆóüƒXƒy[ƒX" + +#: param/loadparm.c:809 +msgid "read size" +msgstr "“Ç‚ŬŽĉ‚èƒTƒCƒY" + +#: param/loadparm.c:811 +msgid "socket options" +msgstr "ƒ\\ƒPƒbƒg ƒIƒvƒVƒ‡ƒ“" + +#: param/loadparm.c:812 +msgid "stat cache size" +msgstr "stat ƒLƒƒƒbƒVƒ… ƒTƒCƒY" + +#: param/loadparm.c:813 +#, fuzzy +msgid "strict allocate" +msgstr "Œµ–§‚ȃƒbƒN" + +#: param/loadparm.c:814 +msgid "strict sync" +msgstr "Œµ–§‚È sync" + +#: param/loadparm.c:815 +msgid "sync always" +msgstr "í‚É sync" + +#: param/loadparm.c:816 +#, fuzzy +msgid "use mmap" +msgstr "ƒ†[ƒU–ĵƒ}ƒbƒv" + +#: param/loadparm.c:817 +msgid "hostname lookups" +msgstr "" + +#: param/loadparm.c:818 +msgid "write cache size" +msgstr "‘‚Ѝž‚ŬƒLƒƒƒbƒVƒ… ƒTƒCƒY" + +#: param/loadparm.c:820 +msgid "Printing Options" +msgstr "ˆóüƒIƒvƒVƒ‡ƒ“" + +#: param/loadparm.c:822 +#, fuzzy +msgid "total print jobs" +msgstr "ƒvƒŠƒ“ƒ^‚ƒ[ƒh" + +#: param/loadparm.c:823 +#, fuzzy +msgid "max print jobs" +msgstr "ˆóü‰Â" + +#: param/loadparm.c:824 +msgid "load printers" +msgstr "ƒvƒŠƒ“ƒ^‚ƒ[ƒh" + +#: param/loadparm.c:825 +msgid "printcap name" +msgstr "printcap –ĵ" + +#: param/loadparm.c:826 +msgid "printcap" +msgstr "printcap" + +#: param/loadparm.c:827 +msgid "printable" +msgstr "ˆóü‰Â" + +#: param/loadparm.c:828 +msgid "print ok" +msgstr "ˆóü‰Â" + +#: param/loadparm.c:829 +msgid "postscript" +msgstr "ƒ|ƒXƒgƒXƒNƒŠƒvƒg" + +#: param/loadparm.c:830 +msgid "printing" +msgstr "ˆóü•û–@" + +#: param/loadparm.c:831 +msgid "print command" +msgstr "ˆóüƒRƒ}ƒ“ƒh" + +#: param/loadparm.c:832 +msgid "disable spoolss" +msgstr "" + +#: param/loadparm.c:833 +msgid "lpq command" +msgstr "lpq ƒRƒ}ƒ“ƒh" + +#: param/loadparm.c:834 +msgid "lprm command" +msgstr "lprm ƒRƒ}ƒ“ƒh" + +#: param/loadparm.c:835 +msgid "lppause command" +msgstr "lppause ƒRƒ}ƒ“ƒh" + +#: param/loadparm.c:836 +msgid "lpresume command" +msgstr "lpresume ƒRƒ}ƒ“ƒh" + +#: param/loadparm.c:837 +msgid "queuepause command" +msgstr "ƒLƒ…[’âŽ~ƒRƒ}ƒ“ƒh" + +#: param/loadparm.c:838 +msgid "queueresume command" +msgstr "ƒLƒ…[ÄŠJƒRƒ}ƒ“ƒh" + +#: param/loadparm.c:840 +#, fuzzy +msgid "enumports command" +msgstr "ˆóüƒRƒ}ƒ“ƒh" + +#: param/loadparm.c:841 +#, fuzzy +msgid "addprinter command" +msgstr "ˆóüƒRƒ}ƒ“ƒh" + +#: param/loadparm.c:842 +#, fuzzy +msgid "deleteprinter command" +msgstr "ˆóüƒRƒ}ƒ“ƒh" + +#: param/loadparm.c:843 +#, fuzzy +msgid "show add printer wizard" +msgstr "ƒvƒŠƒ“ƒ^‚ƒ[ƒh" + +#: param/loadparm.c:844 +#, fuzzy +msgid "os2 driver map" +msgstr "ƒz[ƒ€ƒfƒBƒŒƒNƒgƒŠ ƒ}ƒbƒv" + +#: param/loadparm.c:846 +msgid "printer name" +msgstr "ƒvƒŠƒ“ƒ^–ĵ" + +#: param/loadparm.c:847 +msgid "printer" +msgstr "ƒvƒŠƒ“ƒ^" + +#: param/loadparm.c:848 +#, fuzzy +msgid "use client driver" +msgstr "ssl ƒNƒ‰ƒCƒAƒ“ƒg”FĜ" + +#: param/loadparm.c:849 +msgid "printer driver" +msgstr "ƒvƒŠƒ“ƒ^ ƒhƒ‰ƒCƒo" + +#: param/loadparm.c:850 +msgid "printer driver file" +msgstr "ƒvƒŠƒ“ƒ^ ƒhƒ‰ƒCƒo ƒtƒ@ƒCƒ‹" + +#: param/loadparm.c:851 +msgid "printer driver location" +msgstr "ƒvƒŠƒ“ƒ^ ƒhƒ‰ƒCƒo‚̏ꏊ" + +#: param/loadparm.c:853 +msgid "Filename Handling" +msgstr "ƒtƒ@ƒCƒ‹–ĵ‚ÌŽĉˆµ" + +#: param/loadparm.c:854 +msgid "strip dot" +msgstr "ƒhƒbƒg‚í‚é" + +#: param/loadparm.c:856 +msgid "mangled stack" +msgstr "–ĵ‘O•ÏŠ·—pƒXƒ^ƒbƒN" + +#: param/loadparm.c:857 +msgid "default case" +msgstr "Šù’è‚Ì•ĥŽš‚Ì‘ċĴ" + +#: param/loadparm.c:858 +msgid "case sensitive" +msgstr "‘ċ/Ĵ•ĥŽš‚Ì‹ĉ•Ê" + +#: param/loadparm.c:859 +msgid "casesignames" +msgstr "‘ċ/Ĵ•ĥŽš‚Ì‹ĉ•Ê" + +#: param/loadparm.c:860 +msgid "preserve case" +msgstr "•ĥŽš‚Ì‘ċĴ‚•Û‘ĥ" + +#: param/loadparm.c:861 +msgid "short preserve case" +msgstr "’ZŒ`Ž‚Ċ•ĥŽš‚Ì‘ċĴ‚•Û‘ĥ" + +#: param/loadparm.c:862 +msgid "mangle case" +msgstr "‘ċ/Ĵ•ĥŽš‚Ì•ÏŠ·" + +#: param/loadparm.c:863 +msgid "mangling char" +msgstr "•ÏŠ·—p•ĥŽš" + +#: param/loadparm.c:864 +msgid "hide dot files" +msgstr "ƒhƒbƒgƒtƒ@ƒCƒ‹‚‰B‚·" + +#: param/loadparm.c:865 +msgid "hide unreadable" +msgstr "" + +#: param/loadparm.c:866 +msgid "delete veto files" +msgstr "‹‘”Ûƒtƒ@ƒCƒ‹‚íœ" + +#: param/loadparm.c:867 +msgid "veto files" +msgstr "‹‘”Ûƒtƒ@ƒCƒ‹" + +#: param/loadparm.c:868 +msgid "hide files" +msgstr "‰B‚µƒtƒ@ƒCƒ‹" + +#: param/loadparm.c:869 +msgid "veto oplock files" +msgstr "oplock ‚‹ÖŽ~‚·‚éƒtƒ@ƒCƒ‹" + +#: param/loadparm.c:870 +msgid "map system" +msgstr "ƒVƒXƒeƒ€‘Ğ‚Ƀ}ƒbƒv" + +#: param/loadparm.c:871 +msgid "map hidden" +msgstr "‰B‚µ‘Ğ‚Ƀ}ƒbƒv" + +#: param/loadparm.c:872 +msgid "map archive" +msgstr "ƒA[ƒJƒCƒu‘Ğ‚Ƀ}ƒbƒv" + +#: param/loadparm.c:873 +msgid "mangled names" +msgstr "•ÏŠ·‚µ‚½–ĵ‘O‚Ċ•\ŽĤ" + +#: param/loadparm.c:874 +msgid "mangled map" +msgstr "•ÏŠ·ƒ}ƒbƒv" + +#: param/loadparm.c:875 +msgid "stat cache" +msgstr "stat ƒLƒƒƒbƒVƒ…" + +#: param/loadparm.c:877 +msgid "Domain Options" +msgstr "ƒhƒƒCƒ“ ƒIƒvƒVƒ‡ƒ“" + +#: param/loadparm.c:879 +msgid "domain admin group" +msgstr "ƒhƒƒCƒ“ŠÇ—ƒOƒ‹[ƒv" + +#: param/loadparm.c:880 +msgid "domain guest group" +msgstr "ƒhƒƒCƒ“ ƒQƒXƒg ƒOƒ‹[ƒv" + +#: param/loadparm.c:883 +msgid "groupname map" +msgstr "ƒOƒ‹[ƒv–ĵƒ}ƒbƒv" + +#: param/loadparm.c:886 +msgid "machine password timeout" +msgstr "ƒ}ƒVƒ“ ƒpƒXƒ[ƒh ƒ^ƒCƒ€ƒAƒEƒg" + +#: param/loadparm.c:888 +msgid "Logon Options" +msgstr "ƒƒOƒIƒ“ ƒIƒvƒVƒ‡ƒ“" + +#: param/loadparm.c:890 +msgid "add user script" +msgstr "ƒ†[ƒU’ljÁƒXƒNƒŠƒvƒg" + +#: param/loadparm.c:891 +msgid "delete user script" +msgstr "ƒ†[ƒUíœƒXƒNƒŠƒvƒg" + +#: param/loadparm.c:892 +#, fuzzy +msgid "add group script" +msgstr "ƒ†[ƒU’ljÁƒXƒNƒŠƒvƒg" + +#: param/loadparm.c:893 +#, fuzzy +msgid "delete group script" +msgstr "ƒ†[ƒUíœƒXƒNƒŠƒvƒg" + +#: param/loadparm.c:894 +#, fuzzy +msgid "add user to group script" +msgstr "ƒ†[ƒU’ljÁƒXƒNƒŠƒvƒg" + +#: param/loadparm.c:895 +#, fuzzy +msgid "delete user from group script" +msgstr "ƒ†[ƒUíœƒXƒNƒŠƒvƒg" + +#: param/loadparm.c:896 +#, fuzzy +msgid "add machine script" +msgstr "ƒ†[ƒU’ljÁƒXƒNƒŠƒvƒg" + +#: param/loadparm.c:897 +#, fuzzy +msgid "shutdown script" +msgstr "ƒƒOƒIƒ“ ƒXƒNƒŠƒvƒg" + +#: param/loadparm.c:898 +#, fuzzy +msgid "abort shutdown script" +msgstr "ƒƒOƒIƒ“ ƒXƒNƒŠƒvƒg" + +#: param/loadparm.c:900 +msgid "logon script" +msgstr "ƒƒOƒIƒ“ ƒXƒNƒŠƒvƒg" + +#: param/loadparm.c:901 +msgid "logon path" +msgstr "ƒƒOƒIƒ“ ƒpƒX" + +#: param/loadparm.c:902 +msgid "logon drive" +msgstr "ƒƒOƒIƒ“ ƒhƒ‰ƒCƒu" + +#: param/loadparm.c:903 +msgid "logon home" +msgstr "ƒƒOƒIƒ“ ƒz[ƒ€" + +#: param/loadparm.c:904 +msgid "domain logons" +msgstr "ƒhƒƒCƒ“ ƒƒOƒIƒ“" + +#: param/loadparm.c:906 +msgid "Browse Options" +msgstr "ƒRƒ“ƒsƒ…[ƒ^ˆê——•\ŽĤƒIƒvƒVƒ‡ƒ“" + +#: param/loadparm.c:908 +msgid "os level" +msgstr "os ƒŒƒxƒ‹" + +#: param/loadparm.c:909 +msgid "lm announce" +msgstr "lm ƒAƒiƒEƒ“ƒX" + +#: param/loadparm.c:910 +msgid "lm interval" +msgstr "lm ŠÔŠu" + +#: param/loadparm.c:911 +msgid "preferred master" +msgstr "—Dĉ‚·‚éƒ}ƒXƒ^" + +#: param/loadparm.c:912 +msgid "prefered master" +msgstr "—Dĉ‚·‚éƒ}ƒXƒ^" + +#: param/loadparm.c:913 +msgid "local master" +msgstr "ƒ[ƒJƒ‹ ƒ}ƒXƒ^" + +#: param/loadparm.c:914 +msgid "domain master" +msgstr "ƒhƒƒCƒ“ ƒ}ƒXƒ^" + +#: param/loadparm.c:915 +msgid "browse list" +msgstr "ƒuƒ‰ƒEƒY ƒŠƒXƒg" + +#: param/loadparm.c:916 +msgid "browseable" +msgstr "ƒuƒ‰ƒEƒY‰Â" + +#: param/loadparm.c:917 +msgid "browsable" +msgstr "ƒuƒ‰ƒEƒY‰Â" + +#: param/loadparm.c:918 +msgid "enhanced browsing" +msgstr "" + +#: param/loadparm.c:920 +msgid "WINS Options" +msgstr "WINSƒIƒvƒVƒ‡ƒ“" + +#: param/loadparm.c:921 +msgid "dns proxy" +msgstr "dns ƒvƒƒLƒV" + +#: param/loadparm.c:922 +msgid "wins proxy" +msgstr "wins ƒvƒƒLƒV" + +#: param/loadparm.c:924 +msgid "wins server" +msgstr "wins ƒT[ƒo" + +#: param/loadparm.c:925 +msgid "wins support" +msgstr "wins ƒTƒ|[ƒg" + +#: param/loadparm.c:926 +msgid "wins hook" +msgstr "wins ƒtƒbƒN" + +#: param/loadparm.c:928 +msgid "Locking Options" +msgstr "ƒƒbƒLƒ“ƒO ƒIƒvƒVƒ‡ƒ“" + +#: param/loadparm.c:930 +#, fuzzy +msgid "blocking locks" +msgstr "ƒƒbƒN" + +#: param/loadparm.c:931 +msgid "fake oplocks" +msgstr "‹U‘• oplock" + +#: param/loadparm.c:932 +msgid "kernel oplocks" +msgstr "ƒJ[ƒlƒ‹ oplock" + +#: param/loadparm.c:933 +msgid "locking" +msgstr "ƒƒbƒN" + +#: param/loadparm.c:935 +msgid "oplocks" +msgstr "•Ö‹X“IƒƒbƒN" + +#: param/loadparm.c:936 +msgid "level2 oplocks" +msgstr "level2 oplocks" + +#: param/loadparm.c:937 +msgid "oplock break wait time" +msgstr "oplock ’†’f‚Ì‘Ò‚żŽžŠÔ" + +#: param/loadparm.c:938 +msgid "oplock contention limit" +msgstr "oplock ‹£‡‚ÌŒÀ“x" + +#: param/loadparm.c:939 +#, fuzzy +msgid "posix locking" +msgstr "Œµ–§‚ȃƒbƒN" + +#: param/loadparm.c:940 +msgid "strict locking" +msgstr "Œµ–§‚ȃƒbƒN" + +#: param/loadparm.c:941 +msgid "share modes" +msgstr "‹¤—Lƒ‚[ƒh" + +#: param/loadparm.c:944 +msgid "Ldap Options" +msgstr "Ldap ƒIƒvƒVƒ‡ƒ“" + +#: param/loadparm.c:946 +msgid "ldap server" +msgstr "ldap ƒT[ƒo" + +#: param/loadparm.c:947 +msgid "ldap port" +msgstr "ldap ƒ|[ƒg" + +#: param/loadparm.c:948 +msgid "ldap suffix" +msgstr "lpad ƒTƒtƒBƒbƒNƒX" + +#: param/loadparm.c:949 +msgid "ldap filter" +msgstr "ldap ƒtƒBƒ‹ƒ^[" + +#: param/loadparm.c:950 +msgid "ldap root" +msgstr "ldap ƒ‹[ƒg" + +#: param/loadparm.c:951 +msgid "ldap root passwd" +msgstr "ldap ƒ‹[ƒg ƒpƒXƒ[ƒh" + +#: param/loadparm.c:954 +msgid "Miscellaneous Options" +msgstr "‚ğ‚Ì‘ĵ‚̃IƒvƒVƒ‡ƒ“" + +#: param/loadparm.c:955 +#, fuzzy +msgid "add share command" +msgstr "dfree ƒRƒ}ƒ“ƒh" + +#: param/loadparm.c:956 +#, fuzzy +msgid "change share command" +msgstr "ƒƒbƒZ[ƒW ƒRƒ}ƒ“ƒh" + +#: param/loadparm.c:957 +#, fuzzy +msgid "delete share command" +msgstr "ƒƒbƒZ[ƒW ƒRƒ}ƒ“ƒh" + +#: param/loadparm.c:959 +msgid "config file" +msgstr "Ŭ’èƒtƒ@ƒCƒ‹" + +#: param/loadparm.c:960 +msgid "preload" +msgstr "ƒvƒŠƒ[ƒh" + +#: param/loadparm.c:961 +msgid "auto services" +msgstr "Žİ“ƒT[ƒrƒX" + +#: param/loadparm.c:962 +msgid "lock dir" +msgstr "ƒƒbƒN ƒfƒBƒŒƒNƒgƒŠ" + +#: param/loadparm.c:963 +msgid "lock directory" +msgstr "ƒƒbƒN ƒfƒBƒŒƒNƒgƒŠ" + +#: param/loadparm.c:965 +msgid "utmp directory" +msgstr "utmp ƒfƒBƒŒƒNƒgƒŠ" + +#: param/loadparm.c:966 +#, fuzzy +msgid "wtmp directory" +msgstr "utmp ƒfƒBƒŒƒNƒgƒŠ" + +#: param/loadparm.c:967 +#, fuzzy +msgid "utmp" +msgstr "utmp ƒfƒBƒŒƒNƒgƒŠ" + +#: param/loadparm.c:970 +msgid "default service" +msgstr "Šù’èƒT[ƒrƒX" + +#: param/loadparm.c:971 +msgid "default" +msgstr "Šù’è" + +#: param/loadparm.c:972 +msgid "message command" +msgstr "ƒƒbƒZ[ƒW ƒRƒ}ƒ“ƒh" + +#: param/loadparm.c:973 +msgid "dfree command" +msgstr "dfree ƒRƒ}ƒ“ƒh" + +#: param/loadparm.c:974 +msgid "remote announce" +msgstr "ƒŠƒ‚[ƒg ƒAƒiƒEƒ“ƒX" + +#: param/loadparm.c:975 +msgid "remote browse sync" +msgstr "ƒŠƒ‚[ƒg‚̃uƒ‰ƒEƒYƒŠƒXƒg‚“ŻŠú" + +#: param/loadparm.c:976 +msgid "socket address" +msgstr "ƒ\\ƒPƒbƒg ƒAƒhƒŒƒX" + +#: param/loadparm.c:977 +msgid "homedir map" +msgstr "ƒz[ƒ€ƒfƒBƒŒƒNƒgƒŠ ƒ}ƒbƒv" + +#: param/loadparm.c:978 +msgid "time offset" +msgstr "ŽžŠÔƒIƒtƒZƒbƒg" + +#: param/loadparm.c:979 +msgid "NIS homedir" +msgstr "NIS ƒz[ƒ€ƒfƒBƒŒƒNƒgƒŠ" + +#: param/loadparm.c:980 +msgid "-valid" +msgstr "-valid" + +#: param/loadparm.c:982 +msgid "copy" +msgstr "ƒRƒs[" + +#: param/loadparm.c:983 +msgid "include" +msgstr "ƒCƒ“ƒNƒ‹[ƒh" + +#: param/loadparm.c:984 +msgid "exec" +msgstr "ŽÀs" + +#: param/loadparm.c:985 +msgid "preexec" +msgstr "Ú‘ħŽž‚ÉŽÀs" + +#: param/loadparm.c:987 +#, fuzzy +msgid "preexec close" +msgstr "Ú‘ħŽž‚ÉŽÀs" + +#: param/loadparm.c:988 +msgid "postexec" +msgstr "Ĝ’fŽž‚ÉŽÀs" + +#: param/loadparm.c:989 +msgid "root preexec" +msgstr "ƒ‹[ƒg‚ĊÚ‘ħŽžŽÀs" + +#: param/loadparm.c:990 +#, fuzzy +msgid "root preexec close" +msgstr "ƒ‹[ƒg‚ĊÚ‘ħŽžŽÀs" + +#: param/loadparm.c:991 +msgid "root postexec" +msgstr "ƒ‹[ƒg‚ĊĜ’fŽžŽÀs" + +#: param/loadparm.c:992 +msgid "available" +msgstr "—˜—p‰Â”\" + +#: param/loadparm.c:993 +msgid "volume" +msgstr "ƒ{ƒŠƒ…[ƒ€" + +#: param/loadparm.c:994 +msgid "fstype" +msgstr "ƒtƒ@ƒCƒ‹ ƒVƒXƒeƒ€ ƒ^ƒCƒv" + +#: param/loadparm.c:995 +#, fuzzy +msgid "set directory" +msgstr "ƒfƒBƒŒƒNƒgƒŠ" + +#: param/loadparm.c:996 +msgid "source environment" +msgstr "" + +#: param/loadparm.c:997 +msgid "wide links" +msgstr "L‚­ƒŠƒ“ƒN" + +#: param/loadparm.c:998 +msgid "follow symlinks" +msgstr "symlink ĉ‚ŽQĈ" + +#: param/loadparm.c:999 +msgid "dont descend" +msgstr "‰ş‚ɍ~‚è‚È‚˘ƒfƒBƒŒƒNƒgƒŠ" + +#: param/loadparm.c:1000 +msgid "magic script" +msgstr "ƒ}ƒWƒbƒN ƒXƒNƒŠƒvƒg" + +#: param/loadparm.c:1001 +msgid "magic output" +msgstr "ƒ}ƒWƒbƒN o—Í" + +#: param/loadparm.c:1002 +msgid "delete readonly" +msgstr "“Ç‚ŬŽĉ‚è‚Ì‚Ŭ‚̃tƒ@ƒCƒ‹‚íœ" + +#: param/loadparm.c:1003 +#, fuzzy +msgid "dos filemode" +msgstr "dos ‚̃tƒ@ƒCƒ‹Žž" + +#: param/loadparm.c:1004 +msgid "dos filetimes" +msgstr "dos ‚̃tƒ@ƒCƒ‹Žž" + +#: param/loadparm.c:1005 +msgid "dos filetime resolution" +msgstr "dos ‚̃tƒ@ƒCƒ‹Žž‚̕މ”\" + +#: param/loadparm.c:1007 +msgid "fake directory create times" +msgstr "‹U‚̃fƒBƒŒƒNƒgƒŠìĴŽž" + +#: param/loadparm.c:1008 +msgid "panic action" +msgstr "ƒpƒjƒbƒN ƒAƒNƒVƒ‡ƒ“" + +#: param/loadparm.c:1009 +#, fuzzy +msgid "hide local users" +msgstr "ƒ[ƒJƒ‹ ƒ}ƒXƒ^" + +#: param/loadparm.c:1012 +msgid "VFS options" +msgstr "VFSƒIƒvƒVƒ‡ƒ“" + +#: param/loadparm.c:1014 +msgid "vfs object" +msgstr "" + +#: param/loadparm.c:1015 +#, fuzzy +msgid "vfs options" +msgstr "VFSƒIƒvƒVƒ‡ƒ“" + +#: param/loadparm.c:1018 +#, fuzzy +msgid "msdfs root" +msgstr "ldap ƒ‹[ƒg" + +#: param/loadparm.c:1019 +#, fuzzy +msgid "host msdfs" +msgstr "‹‘”Û‚·‚éƒzƒXƒg" + +#: param/loadparm.c:1021 +msgid "Winbind options" +msgstr "WinbindƒIƒvƒVƒ‡ƒ“" + +#: param/loadparm.c:1023 +#, fuzzy +msgid "winbind uid" +msgstr "WinbindƒIƒvƒVƒ‡ƒ“" + +#: param/loadparm.c:1024 +#, fuzzy +msgid "winbind gid" +msgstr "WinbindƒIƒvƒVƒ‡ƒ“" + +#: param/loadparm.c:1025 +msgid "template homedir" +msgstr "" + +#: param/loadparm.c:1026 +msgid "template shell" +msgstr "" + +#: param/loadparm.c:1027 +#, fuzzy +msgid "winbind separator" +msgstr "WinbindƒIƒvƒVƒ‡ƒ“" + +#: param/loadparm.c:1028 +#, fuzzy +msgid "winbind cache time" +msgstr "lpq ƒLƒƒƒbƒVƒ…ŽžŠÔ" + +#: param/loadparm.c:1029 +#, fuzzy +msgid "winbind enum users" +msgstr "–³Œĝ‚ȃ†[ƒU" + +#: param/loadparm.c:1030 +msgid "winbind enum groups" +msgstr "" + +#~ msgid "failed to open %s for writing\n" +#~ msgstr "%s ‚‘‚Ѝž‚Ŭ—p‚ɃI[ƒvƒ“‚Ċ‚Ğ‚Ü‚ı‚ñ\n" + +#~ msgid "Can't reload %s\n" +#~ msgstr "%s ‚Ä“Ç‚Ŭž‚Ŭ‚Ċ‚Ğ‚Ü‚ı‚ñ\n" + +#~ msgid "Can't setup password database vectors.\n" +#~ msgstr "ƒpƒXƒ[ƒhEƒf[ƒ^ƒx[ƒX‚ތ݂‚Ż‚ç‚ê‚Ü‚ı‚ñ\n" + +#~ msgid "You need to have status=yes in your smb config file\n" +#~ msgstr "smb.conf ‚Ċ status=yes ‚Ŭ’肵‚Ä‚­‚‚³‚˘\n" + +#~ msgid "coding system" +#~ msgstr "ƒR[ƒfƒBƒ“ƒO ƒVƒXƒeƒ€" + +#~ msgid "client code page" +#~ msgstr "ƒNƒ‰ƒCƒAƒ“ƒg ƒR[ƒhƒy[ƒW" + +#~ msgid "revalidate" +#~ msgstr "Ä”FĜ" + +#~ msgid "status" +#~ msgstr "ƒXƒe[ƒ^ƒX" + +#~ msgid "shared mem size" +#~ msgstr "‹¤—Lƒƒ‚ƒŠ ƒTƒCƒY" + +#~ msgid "character set" +#~ msgstr "•ĥŽšƒZƒbƒg" + +#~ msgid "domain groups" +#~ msgstr "ƒhƒƒCƒ“ ƒOƒ‹[ƒv" + +#~ msgid "domain admin users" +#~ msgstr "ƒhƒƒCƒ“ŠÇ—ƒ†[ƒU" + +#~ msgid "domain guest users" +#~ msgstr "ƒhƒƒCƒ“ ƒQƒXƒg ƒ†[ƒU" + +#~ msgid "ole locking compatibility" +#~ msgstr "ole ƒƒbƒN‚ÌŒŬŠ·Ğ" + +#~ msgid "smbrun" +#~ msgstr "smbrun" + +#, fuzzy +#~ msgid "wtmp dir" +#~ msgstr "utmp ƒfƒBƒŒƒNƒgƒŠ" + +#~ msgid "unix realname" +#~ msgstr "unix ‚Ì–{–ĵ" diff --git a/source4/po/pl.msg b/source4/po/pl.msg new file mode 100644 index 0000000000..c547a94c93 --- /dev/null +++ b/source4/po/pl.msg @@ -0,0 +1,1773 @@ +# Polish messages for international release of SWAT. +# Copyright (C) 2001 Rafal Szczesniak +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the 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: 2001-09-20 20:29+0900\n" +"PO-Revision-Date: 2001-08-15 22:45+02:00\n" +"Last-Translator: Rafal Szczesniak \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:120 +#, c-format +msgid "ERROR: Can't open %s\n" +msgstr "B£ĦD: Nie można otworzyĉ %s\n" + +#. +#. str = stripspace(parm->label); +#. strlower (str); //monyo +#. d_printf("%s       %s", +#. str, _("Help"), parm->label); +#. +#: web/swat.c:211 +msgid "Help" +msgstr "Pomoc" + +#: web/swat.c:217 web/swat.c:231 web/swat.c:246 web/swat.c:254 web/swat.c:263 +#: web/swat.c:272 web/swat.c:278 web/swat.c:284 web/swat.c:297 +msgid "Set Default" +msgstr "Ustaw domyĥlnie" + +#: web/swat.c:502 +#, c-format +msgid "Logged in as %s

\n" +msgstr "Zalogowany jako %s

\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:512 +msgid "Status" +msgstr "Status" + +#: web/swat.c:513 +msgid "View Config" +msgstr "Przejrzyj Konfiguracjê" + +#: web/swat.c:515 +msgid "Password Management" +msgstr "Zarzħdzanie Has³ami" + +#: web/swat.c:539 +msgid "Current Config" +msgstr "Bieżħca Konfiguracja" + +#: web/swat.c:543 +msgid "Normal View" +msgstr "Normalny Widok" + +#: web/swat.c:545 +msgid "Full View" +msgstr "Pe³ny Widok" + +#: web/swat.c:561 +msgid "Global Variables" +msgstr "Zmienne Globalne" + +#: web/swat.c:575 web/swat.c:671 web/swat.c:1014 +msgid "Commit Changes" +msgstr "Potwierdĵ Zmiany" + +#: web/swat.c:579 web/swat.c:674 web/swat.c:1016 +msgid "Reset Values" +msgstr "Zresetuj Wartoĥci" + +#: web/swat.c:581 web/swat.c:676 web/swat.c:1018 +msgid "Advanced View" +msgstr "Widok Zaawansowany" + +#: web/swat.c:583 web/swat.c:678 web/swat.c:1020 +msgid "Basic View" +msgstr "Widok Podstawowy" + +#: web/swat.c:613 +msgid "Share Parameters" +msgstr "Parametry Wspó³udzia³u" + +#: web/swat.c:642 +msgid "Choose Share" +msgstr "Wybierz Wspó³udzia³" + +#: web/swat.c:656 +msgid "Delete Share" +msgstr "Usuñ Wspó³udzia³" + +#: web/swat.c:663 +msgid "Create Share" +msgstr "Utwórz Wspó³udzia³" + +#: web/swat.c:708 +msgid "password change in demo mode rejected\n" +msgstr "zmiana has³a w trybie demo odrzucona\n" + +#: web/swat.c:747 +msgid " Must specify \"User Name\" \n" +msgstr " Musisz podaĉ \"Nazwê Użytkownika\" \n" + +#: web/swat.c:763 +msgid " Must specify \"Old Password\" \n" +msgstr " Musisz podaĉ \"Stare Has³o\" \n" + +#: web/swat.c:769 +msgid " Must specify \"Remote Machine\" \n" +msgstr " Musisz podaĉ \"Zdalnħ Maszynê\" \n" + +#: web/swat.c:776 +msgid " Must specify \"New, and Re-typed Passwords\" \n" +msgstr " Musisz podaĉ \"Nowe Has³o, i ponownie wpisane Nowe Has³o\" \n" + +#: web/swat.c:782 +msgid " Re-typed password didn't match new password\n" +msgstr " Ponownie wpisane has³o nie pasuje do nowego has³a\n" + +#: web/swat.c:812 +#, c-format +msgid " The passwd for '%s' has been changed. \n" +msgstr " Has³o dla '%s' zosta³o zmienione. \n" + +#: web/swat.c:814 +#, c-format +msgid " The passwd for '%s' has NOT been changed. \n" +msgstr " Has³o dla '%s' NIE zosta³o zmienione. \n" + +#: web/swat.c:838 +msgid "Server Password Management" +msgstr "Zarzħdzanie Has³ami na Serwerze" + +#. +#. * Create all the dialog boxes for data collection +#. +#: web/swat.c:847 web/swat.c:894 +msgid " User Name : " +msgstr " Nazwa Użytkownika : " + +#: web/swat.c:850 web/swat.c:896 +msgid " Old Password : " +msgstr " Stare Has³o : " + +#: web/swat.c:853 web/swat.c:898 +msgid " New Password : " +msgstr " Nowe Has³o : " + +#: web/swat.c:855 web/swat.c:900 +msgid " Re-type New Password : " +msgstr " Ponownie wpisz Nowe Has³o : " + +#: web/swat.c:863 web/swat.c:911 +msgid "Change Password" +msgstr "Zmieñ Has³o" + +#: web/swat.c:866 +msgid "Add New User" +msgstr "Dodaj Nowego Użytkownika" + +#: web/swat.c:868 +msgid "Delete User" +msgstr "Usuñ Użytkownika" + +#: web/swat.c:870 +msgid "Disable User" +msgstr "Zablokuj Użytkownika" + +#: web/swat.c:872 +msgid "Enable User" +msgstr "Odblokuj Użytkownika" + +#: web/swat.c:885 +msgid "Client/Server Password Management" +msgstr "Zarzħdzanie Has³ami Klient/Serwer" + +#: web/swat.c:902 +msgid " Remote Machine : " +msgstr " Zdalna Maszyna : " + +#: web/swat.c:940 +msgid "Printer Parameters" +msgstr "Parametry Drukarki" + +#: web/swat.c:942 +msgid "Important Note:" +msgstr "Ważna Informacja:" + +#: web/swat.c:943 +msgid "Printer names marked with [*] in the Choose Printer drop-down box " +msgstr "Nazwy Drukarek zaznaczone [*] w rozwijanym polu Wybierz Drukarkê " + +#: web/swat.c:944 +msgid "are autoloaded printers from " +msgstr "sħ drukarkami automatycznie ³adowanymi z " + +#: web/swat.c:945 +msgid "Printcap Name" +msgstr "Nazwa Printcap" + +#: web/swat.c:946 +msgid "Attempting to delete these printers from SWAT will have no effect.\n" +msgstr "Próby usuniêcia tych drukarek ze SWAT nie przyniosħ efektu.\n" + +#: web/swat.c:980 +msgid "Choose Printer" +msgstr "Wybierz Drukarkê" + +#: web/swat.c:999 +msgid "Delete Printer" +msgstr "Usuñ Drukarkê" + +#: web/swat.c:1006 +msgid "Create Printer" +msgstr "Utwórz Drukarkê" + +#: web/statuspage.c:40 +msgid "DENY_NONE" +msgstr "" + +#: web/statuspage.c:41 +msgid "DENY_ALL " +msgstr "" + +#: web/statuspage.c:42 +msgid "DENY_DOS " +msgstr "" + +#: web/statuspage.c:43 +msgid "DENY_READ " +msgstr "" + +#: web/statuspage.c:44 +msgid "DENY_WRITE " +msgstr "" + +#: web/statuspage.c:50 +msgid "RDONLY " +msgstr "" + +#: web/statuspage.c:51 +msgid "WRONLY " +msgstr "" + +#: web/statuspage.c:52 +msgid "RDWR " +msgstr "" + +#: web/statuspage.c:60 +msgid "EXCLUSIVE+BATCH " +msgstr "" + +#: web/statuspage.c:62 +msgid "EXCLUSIVE " +msgstr "" + +#: web/statuspage.c:64 +msgid "BATCH " +msgstr "" + +#: web/statuspage.c:66 +msgid "LEVEL_II " +msgstr "" + +#: web/statuspage.c:68 +msgid "NONE " +msgstr "" + +#: web/statuspage.c:195 +msgid "Server Status" +msgstr "Status Serwera" + +#: web/statuspage.c:200 +msgid "Auto Refresh" +msgstr "Automatyczne Odĥwieżanie" + +#: web/statuspage.c:201 web/statuspage.c:206 +msgid "Refresh Interval: " +msgstr "Interwa³ Odĥwieżania: " + +#: web/statuspage.c:205 +msgid "Stop Refreshing" +msgstr "Zatrzymaj Odĥwieżanie" + +#: web/statuspage.c:220 +msgid "version:" +msgstr "wersja:" + +#: web/statuspage.c:223 +msgid "smbd:" +msgstr "" + +#: web/statuspage.c:223 web/statuspage.c:235 +msgid "running" +msgstr "dzia³a" + +#: web/statuspage.c:223 web/statuspage.c:235 +msgid "not running" +msgstr "nie dzia³a" + +#: web/statuspage.c:226 +msgid "Stop smbd" +msgstr "Zatrzymaj smbd" + +#: web/statuspage.c:228 +msgid "Start smbd" +msgstr "Uruchom smbd" + +#: web/statuspage.c:230 +msgid "Restart smbd" +msgstr "Zrestartuj smbd" + +#: web/statuspage.c:235 +msgid "nmbd:" +msgstr "" + +#: web/statuspage.c:238 +msgid "Stop nmbd" +msgstr "Zatrzymaj nmbd" + +#: web/statuspage.c:240 +msgid "Start nmbd" +msgstr "Uruchom nmbd" + +#: web/statuspage.c:242 +msgid "Restart nmbd" +msgstr "Zrestartuj nmbd" + +#: web/statuspage.c:249 +msgid "Active Connections" +msgstr "Aktywne Po³ħczenia" + +#: web/statuspage.c:251 web/statuspage.c:264 web/statuspage.c:272 +msgid "PID" +msgstr "" + +#: web/statuspage.c:251 web/statuspage.c:264 +msgid "Client" +msgstr "Klient" + +#: web/statuspage.c:251 +msgid "IP address" +msgstr "adres IP" + +#: web/statuspage.c:251 web/statuspage.c:264 web/statuspage.c:272 +msgid "Date" +msgstr "Data" + +#: web/statuspage.c:253 +msgid "Kill" +msgstr "Zatrzymaj" + +#: web/statuspage.c:261 +msgid "Active Shares" +msgstr "Aktywne Wspó³udzia³y" + +#: web/statuspage.c:264 +msgid "Share" +msgstr "Wspó³udzia³" + +#: web/statuspage.c:264 +msgid "User" +msgstr "Użytkownik" + +#: web/statuspage.c:264 +msgid "Group" +msgstr "Grupa" + +#: web/statuspage.c:270 +msgid "Open Files" +msgstr "Otwarte Pliki" + +#: web/statuspage.c:272 +msgid "Sharing" +msgstr "Wspó³dzielenie" + +#: web/statuspage.c:272 +msgid "R/W" +msgstr "" + +#: web/statuspage.c:272 +msgid "Oplock" +msgstr "" + +#: web/statuspage.c:272 +msgid "File" +msgstr "Plik" + +#: param/loadparm.c:641 +msgid "Base Options" +msgstr "Bazowe Opcje" + +#: param/loadparm.c:643 +#, fuzzy +msgid "dos charset" +msgstr "Wybierz Wspó³udzia³" + +#: param/loadparm.c:644 +msgid "unix charset" +msgstr "" + +#: param/loadparm.c:645 +msgid "display charset" +msgstr "" + +#: param/loadparm.c:646 +msgid "comment" +msgstr "" + +#: param/loadparm.c:647 +msgid "path" +msgstr "" + +#: param/loadparm.c:648 +msgid "directory" +msgstr "" + +#: param/loadparm.c:649 +msgid "workgroup" +msgstr "" + +#: param/loadparm.c:650 +msgid "netbios name" +msgstr "" + +#: param/loadparm.c:651 +msgid "netbios aliases" +msgstr "" + +#: param/loadparm.c:652 +msgid "netbios scope" +msgstr "" + +#: param/loadparm.c:653 +msgid "server string" +msgstr "" + +#: param/loadparm.c:654 +#, fuzzy +msgid "interfaces" +msgstr "Drukarki" + +#: param/loadparm.c:655 +msgid "bind interfaces only" +msgstr "" + +#: param/loadparm.c:657 +msgid "Security Options" +msgstr "Opcje Zabezpieczeñ" + +#: param/loadparm.c:659 +msgid "security" +msgstr "" + +#: param/loadparm.c:660 +msgid "encrypt passwords" +msgstr "" + +#: param/loadparm.c:661 +msgid "update encrypted" +msgstr "" + +#: param/loadparm.c:662 +msgid "allow trusted domains" +msgstr "" + +#: param/loadparm.c:663 +msgid "alternate permissions" +msgstr "" + +#: param/loadparm.c:664 +msgid "hosts equiv" +msgstr "" + +#: param/loadparm.c:665 +msgid "min passwd length" +msgstr "" + +#: param/loadparm.c:666 +msgid "min password length" +msgstr "" + +#: param/loadparm.c:667 +msgid "map to guest" +msgstr "" + +#: param/loadparm.c:668 +#, fuzzy +msgid "null passwords" +msgstr "Zmieñ Has³o" + +#: param/loadparm.c:669 +msgid "obey pam restrictions" +msgstr "" + +#: param/loadparm.c:670 +msgid "password server" +msgstr "" + +#: param/loadparm.c:671 +msgid "smb passwd file" +msgstr "" + +#: param/loadparm.c:672 +msgid "private dir" +msgstr "" + +#: param/loadparm.c:673 +msgid "passdb module path" +msgstr "" + +#: param/loadparm.c:674 +msgid "root directory" +msgstr "" + +#: param/loadparm.c:675 +msgid "root dir" +msgstr "" + +#: param/loadparm.c:676 +msgid "root" +msgstr "" + +#: param/loadparm.c:678 +#, fuzzy +msgid "pam password change" +msgstr "Zarzħdzanie Has³ami" + +#: param/loadparm.c:679 +msgid "passwd program" +msgstr "" + +#: param/loadparm.c:680 +msgid "passwd chat" +msgstr "" + +#: param/loadparm.c:681 +msgid "passwd chat debug" +msgstr "" + +#: param/loadparm.c:682 +msgid "username map" +msgstr "" + +#: param/loadparm.c:683 +#, fuzzy +msgid "password level" +msgstr "Zarzħdzanie Has³ami" + +#: param/loadparm.c:684 +msgid "username level" +msgstr "" + +#: param/loadparm.c:685 +msgid "unix password sync" +msgstr "" + +#: param/loadparm.c:686 +msgid "restrict anonymous" +msgstr "" + +#: param/loadparm.c:687 +msgid "lanman auth" +msgstr "" + +#: param/loadparm.c:688 +msgid "ntlm auth" +msgstr "" + +#: param/loadparm.c:689 +msgid "plaintext to smbpasswd" +msgstr "" + +#: param/loadparm.c:690 +msgid "use rhosts" +msgstr "" + +#: param/loadparm.c:692 +msgid "username" +msgstr "" + +#: param/loadparm.c:693 +#, fuzzy +msgid "user" +msgstr "Użytkownik" + +#: param/loadparm.c:694 +#, fuzzy +msgid "users" +msgstr "Użytkownik" + +#: param/loadparm.c:696 +msgid "guest account" +msgstr "" + +#: param/loadparm.c:697 +msgid "invalid users" +msgstr "" + +#: param/loadparm.c:698 +msgid "valid users" +msgstr "" + +#: param/loadparm.c:699 +msgid "admin users" +msgstr "" + +#: param/loadparm.c:700 +msgid "read list" +msgstr "" + +#: param/loadparm.c:701 +msgid "write list" +msgstr "" + +#: param/loadparm.c:702 +msgid "printer admin" +msgstr "" + +#: param/loadparm.c:703 +msgid "force user" +msgstr "" + +#: param/loadparm.c:704 +msgid "force group" +msgstr "" + +#: param/loadparm.c:705 +#, fuzzy +msgid "group" +msgstr "Grupa" + +#: param/loadparm.c:707 +msgid "read only" +msgstr "" + +#: param/loadparm.c:708 +msgid "write ok" +msgstr "" + +#: param/loadparm.c:709 +msgid "writeable" +msgstr "" + +#: param/loadparm.c:710 +msgid "writable" +msgstr "" + +#: param/loadparm.c:712 +#, fuzzy +msgid "create mask" +msgstr "Utwórz Wspó³udzia³" + +#: param/loadparm.c:713 +#, fuzzy +msgid "create mode" +msgstr "Utwórz Wspó³udzia³" + +#: param/loadparm.c:714 +msgid "force create mode" +msgstr "" + +#: param/loadparm.c:715 +#, fuzzy +msgid "security mask" +msgstr "Opcje Zabezpieczeñ" + +#: param/loadparm.c:716 +msgid "force security mode" +msgstr "" + +#: param/loadparm.c:717 +msgid "directory mask" +msgstr "" + +#: param/loadparm.c:718 +msgid "directory mode" +msgstr "" + +#: param/loadparm.c:719 +msgid "force directory mode" +msgstr "" + +#: param/loadparm.c:720 +msgid "directory security mask" +msgstr "" + +#: param/loadparm.c:721 +msgid "force directory security mode" +msgstr "" + +#: param/loadparm.c:722 +msgid "inherit permissions" +msgstr "" + +#: param/loadparm.c:723 +msgid "guest only" +msgstr "" + +#: param/loadparm.c:724 +msgid "only guest" +msgstr "" + +#: param/loadparm.c:726 +msgid "guest ok" +msgstr "" + +#: param/loadparm.c:727 +msgid "public" +msgstr "" + +#: param/loadparm.c:729 +#, fuzzy +msgid "only user" +msgstr "Odblokuj Użytkownika" + +#: param/loadparm.c:730 +msgid "hosts allow" +msgstr "" + +#: param/loadparm.c:731 +msgid "allow hosts" +msgstr "" + +#: param/loadparm.c:732 +msgid "hosts deny" +msgstr "" + +#: param/loadparm.c:733 +msgid "deny hosts" +msgstr "" + +#: param/loadparm.c:736 +msgid "Secure Socket Layer Options" +msgstr "Opcje SSL" + +#: param/loadparm.c:737 +msgid "ssl" +msgstr "" + +#: param/loadparm.c:739 +msgid "ssl hosts" +msgstr "" + +#: param/loadparm.c:740 +msgid "ssl hosts resign" +msgstr "" + +#: param/loadparm.c:741 +msgid "ssl CA certDir" +msgstr "" + +#: param/loadparm.c:742 +msgid "ssl CA certFile" +msgstr "" + +#: param/loadparm.c:743 +msgid "ssl server cert" +msgstr "" + +#: param/loadparm.c:744 +msgid "ssl server key" +msgstr "" + +#: param/loadparm.c:745 +msgid "ssl client cert" +msgstr "" + +#: param/loadparm.c:746 +msgid "ssl client key" +msgstr "" + +#: param/loadparm.c:747 +msgid "ssl require clientcert" +msgstr "" + +#: param/loadparm.c:748 +msgid "ssl require servercert" +msgstr "" + +#: param/loadparm.c:749 +msgid "ssl ciphers" +msgstr "" + +#: param/loadparm.c:750 +#, fuzzy +msgid "ssl version" +msgstr "wersja:" + +#: param/loadparm.c:751 +msgid "ssl compatibility" +msgstr "" + +#: param/loadparm.c:754 +#, fuzzy +msgid "Logging Options" +msgstr "Opcje Blokowania" + +#: param/loadparm.c:755 +msgid "log level" +msgstr "" + +#: param/loadparm.c:756 +msgid "debuglevel" +msgstr "" + +#: param/loadparm.c:757 +msgid "syslog" +msgstr "" + +#: param/loadparm.c:758 +msgid "syslog only" +msgstr "" + +#: param/loadparm.c:759 +msgid "log file" +msgstr "" + +#: param/loadparm.c:761 +msgid "max log size" +msgstr "" + +#: param/loadparm.c:762 +msgid "timestamp logs" +msgstr "" + +#: param/loadparm.c:763 +msgid "debug timestamp" +msgstr "" + +#: param/loadparm.c:764 +msgid "debug hires timestamp" +msgstr "" + +#: param/loadparm.c:765 +msgid "debug pid" +msgstr "" + +#: param/loadparm.c:766 +msgid "debug uid" +msgstr "" + +#: param/loadparm.c:768 +msgid "Protocol Options" +msgstr "Opcje Protoko³u" + +#: param/loadparm.c:770 +msgid "protocol" +msgstr "" + +#: param/loadparm.c:771 +msgid "large readwrite" +msgstr "" + +#: param/loadparm.c:772 +msgid "max protocol" +msgstr "" + +#: param/loadparm.c:773 +msgid "min protocol" +msgstr "" + +#: param/loadparm.c:774 +msgid "unicode" +msgstr "" + +#: param/loadparm.c:775 +msgid "read bmpx" +msgstr "" + +#: param/loadparm.c:776 +msgid "read raw" +msgstr "" + +#: param/loadparm.c:777 +msgid "write raw" +msgstr "" + +#: param/loadparm.c:779 +msgid "nt smb support" +msgstr "" + +#: param/loadparm.c:780 +msgid "nt pipe support" +msgstr "" + +#: param/loadparm.c:781 +msgid "nt acl support" +msgstr "" + +#: param/loadparm.c:782 +msgid "announce version" +msgstr "" + +#: param/loadparm.c:783 +msgid "announce as" +msgstr "" + +#: param/loadparm.c:784 +msgid "max mux" +msgstr "" + +#: param/loadparm.c:785 +msgid "max xmit" +msgstr "" + +#: param/loadparm.c:787 +msgid "name resolve order" +msgstr "" + +#: param/loadparm.c:788 +msgid "max packet" +msgstr "" + +#: param/loadparm.c:789 +msgid "packet size" +msgstr "" + +#: param/loadparm.c:790 +msgid "max ttl" +msgstr "" + +#: param/loadparm.c:791 +msgid "max wins ttl" +msgstr "" + +#: param/loadparm.c:792 +msgid "min wins ttl" +msgstr "" + +#: param/loadparm.c:793 +msgid "time server" +msgstr "" + +#: param/loadparm.c:795 +msgid "Tuning Options" +msgstr "Opcje Dostrajajħce" + +#: param/loadparm.c:797 +msgid "change notify timeout" +msgstr "" + +#: param/loadparm.c:798 +msgid "deadtime" +msgstr "" + +#: param/loadparm.c:799 +msgid "getwd cache" +msgstr "" + +#: param/loadparm.c:800 +msgid "keepalive" +msgstr "" + +#: param/loadparm.c:802 +msgid "lpq cache time" +msgstr "" + +#: param/loadparm.c:803 +msgid "max smbd processes" +msgstr "" + +#: param/loadparm.c:804 +#, fuzzy +msgid "max connections" +msgstr "Aktywne Po³ħczenia" + +#: param/loadparm.c:805 +msgid "paranoid server security" +msgstr "" + +#: param/loadparm.c:806 +msgid "max disk size" +msgstr "" + +#: param/loadparm.c:807 +#, fuzzy +msgid "max open files" +msgstr "Otwarte Pliki" + +#: param/loadparm.c:808 +msgid "min print space" +msgstr "" + +#: param/loadparm.c:809 +msgid "read size" +msgstr "" + +#: param/loadparm.c:811 +#, fuzzy +msgid "socket options" +msgstr "Bazowe Opcje" + +#: param/loadparm.c:812 +msgid "stat cache size" +msgstr "" + +#: param/loadparm.c:813 +msgid "strict allocate" +msgstr "" + +#: param/loadparm.c:814 +msgid "strict sync" +msgstr "" + +#: param/loadparm.c:815 +msgid "sync always" +msgstr "" + +#: param/loadparm.c:816 +msgid "use mmap" +msgstr "" + +#: param/loadparm.c:817 +msgid "hostname lookups" +msgstr "" + +#: param/loadparm.c:818 +msgid "write cache size" +msgstr "" + +#: param/loadparm.c:820 +msgid "Printing Options" +msgstr "Opcje Drukowania" + +#: param/loadparm.c:822 +msgid "total print jobs" +msgstr "" + +#: param/loadparm.c:823 +msgid "max print jobs" +msgstr "" + +#: param/loadparm.c:824 +#, fuzzy +msgid "load printers" +msgstr "Drukarki" + +#: param/loadparm.c:825 +#, fuzzy +msgid "printcap name" +msgstr "Nazwa Printcap" + +#: param/loadparm.c:826 +#, fuzzy +msgid "printcap" +msgstr "Nazwa Printcap" + +#: param/loadparm.c:827 +msgid "printable" +msgstr "" + +#: param/loadparm.c:828 +msgid "print ok" +msgstr "" + +#: param/loadparm.c:829 +msgid "postscript" +msgstr "" + +#: param/loadparm.c:830 +#, fuzzy +msgid "printing" +msgstr "dzia³a" + +#: param/loadparm.c:831 +msgid "print command" +msgstr "" + +#: param/loadparm.c:832 +msgid "disable spoolss" +msgstr "" + +#: param/loadparm.c:833 +msgid "lpq command" +msgstr "" + +#: param/loadparm.c:834 +msgid "lprm command" +msgstr "" + +#: param/loadparm.c:835 +msgid "lppause command" +msgstr "" + +#: param/loadparm.c:836 +msgid "lpresume command" +msgstr "" + +#: param/loadparm.c:837 +msgid "queuepause command" +msgstr "" + +#: param/loadparm.c:838 +msgid "queueresume command" +msgstr "" + +#: param/loadparm.c:840 +msgid "enumports command" +msgstr "" + +#: param/loadparm.c:841 +msgid "addprinter command" +msgstr "" + +#: param/loadparm.c:842 +#, fuzzy +msgid "deleteprinter command" +msgstr "Usuñ Drukarkê" + +#: param/loadparm.c:843 +msgid "show add printer wizard" +msgstr "" + +#: param/loadparm.c:844 +msgid "os2 driver map" +msgstr "" + +#: param/loadparm.c:846 +#, fuzzy +msgid "printer name" +msgstr "Parametry Drukarki" + +#: param/loadparm.c:847 +#, fuzzy +msgid "printer" +msgstr "Drukarki" + +#: param/loadparm.c:848 +msgid "use client driver" +msgstr "" + +#: param/loadparm.c:849 +#, fuzzy +msgid "printer driver" +msgstr "Parametry Drukarki" + +#: param/loadparm.c:850 +msgid "printer driver file" +msgstr "" + +#: param/loadparm.c:851 +msgid "printer driver location" +msgstr "" + +#: param/loadparm.c:853 +msgid "Filename Handling" +msgstr "Obs³uga Nazw Plików" + +#: param/loadparm.c:854 +msgid "strip dot" +msgstr "" + +#: param/loadparm.c:856 +msgid "mangled stack" +msgstr "" + +#: param/loadparm.c:857 +msgid "default case" +msgstr "" + +#: param/loadparm.c:858 +msgid "case sensitive" +msgstr "" + +#: param/loadparm.c:859 +msgid "casesignames" +msgstr "" + +#: param/loadparm.c:860 +msgid "preserve case" +msgstr "" + +#: param/loadparm.c:861 +msgid "short preserve case" +msgstr "" + +#: param/loadparm.c:862 +msgid "mangle case" +msgstr "" + +#: param/loadparm.c:863 +msgid "mangling char" +msgstr "" + +#: param/loadparm.c:864 +msgid "hide dot files" +msgstr "" + +#: param/loadparm.c:865 +msgid "hide unreadable" +msgstr "" + +#: param/loadparm.c:866 +msgid "delete veto files" +msgstr "" + +#: param/loadparm.c:867 +#, fuzzy +msgid "veto files" +msgstr "Otwarte Pliki" + +#: param/loadparm.c:868 +#, fuzzy +msgid "hide files" +msgstr "Otwarte Pliki" + +#: param/loadparm.c:869 +msgid "veto oplock files" +msgstr "" + +#: param/loadparm.c:870 +msgid "map system" +msgstr "" + +#: param/loadparm.c:871 +msgid "map hidden" +msgstr "" + +#: param/loadparm.c:872 +msgid "map archive" +msgstr "" + +#: param/loadparm.c:873 +msgid "mangled names" +msgstr "" + +#: param/loadparm.c:874 +msgid "mangled map" +msgstr "" + +#: param/loadparm.c:875 +msgid "stat cache" +msgstr "" + +#: param/loadparm.c:877 +msgid "Domain Options" +msgstr "Opcje Domeny" + +#: param/loadparm.c:879 +msgid "domain admin group" +msgstr "" + +#: param/loadparm.c:880 +msgid "domain guest group" +msgstr "" + +#: param/loadparm.c:883 +msgid "groupname map" +msgstr "" + +#: param/loadparm.c:886 +msgid "machine password timeout" +msgstr "" + +#: param/loadparm.c:888 +msgid "Logon Options" +msgstr "Opcje Logowania" + +#: param/loadparm.c:890 +msgid "add user script" +msgstr "" + +#: param/loadparm.c:891 +#, fuzzy +msgid "delete user script" +msgstr "Usuñ Użytkownika" + +#: param/loadparm.c:892 +msgid "add group script" +msgstr "" + +#: param/loadparm.c:893 +msgid "delete group script" +msgstr "" + +#: param/loadparm.c:894 +msgid "add user to group script" +msgstr "" + +#: param/loadparm.c:895 +msgid "delete user from group script" +msgstr "" + +#: param/loadparm.c:896 +msgid "add machine script" +msgstr "" + +#: param/loadparm.c:897 +msgid "shutdown script" +msgstr "" + +#: param/loadparm.c:898 +msgid "abort shutdown script" +msgstr "" + +#: param/loadparm.c:900 +msgid "logon script" +msgstr "" + +#: param/loadparm.c:901 +#, fuzzy +msgid "logon path" +msgstr "Opcje Logowania" + +#: param/loadparm.c:902 +msgid "logon drive" +msgstr "" + +#: param/loadparm.c:903 +msgid "logon home" +msgstr "" + +#: param/loadparm.c:904 +#, fuzzy +msgid "domain logons" +msgstr "Opcje Domeny" + +#: param/loadparm.c:906 +msgid "Browse Options" +msgstr "Opcje Przeglħdania" + +#: param/loadparm.c:908 +msgid "os level" +msgstr "" + +#: param/loadparm.c:909 +msgid "lm announce" +msgstr "" + +#: param/loadparm.c:910 +msgid "lm interval" +msgstr "" + +#: param/loadparm.c:911 +msgid "preferred master" +msgstr "" + +#: param/loadparm.c:912 +msgid "prefered master" +msgstr "" + +#: param/loadparm.c:913 +msgid "local master" +msgstr "" + +#: param/loadparm.c:914 +msgid "domain master" +msgstr "" + +#: param/loadparm.c:915 +#, fuzzy +msgid "browse list" +msgstr "Opcje Przeglħdania" + +#: param/loadparm.c:916 +msgid "browseable" +msgstr "" + +#: param/loadparm.c:917 +msgid "browsable" +msgstr "" + +#: param/loadparm.c:918 +msgid "enhanced browsing" +msgstr "" + +#: param/loadparm.c:920 +msgid "WINS Options" +msgstr "Opcje WINS" + +#: param/loadparm.c:921 +msgid "dns proxy" +msgstr "" + +#: param/loadparm.c:922 +msgid "wins proxy" +msgstr "" + +#: param/loadparm.c:924 +msgid "wins server" +msgstr "" + +#: param/loadparm.c:925 +msgid "wins support" +msgstr "" + +#: param/loadparm.c:926 +msgid "wins hook" +msgstr "" + +#: param/loadparm.c:928 +msgid "Locking Options" +msgstr "Opcje Blokowania" + +#: param/loadparm.c:930 +#, fuzzy +msgid "blocking locks" +msgstr "Opcje Blokowania" + +#: param/loadparm.c:931 +msgid "fake oplocks" +msgstr "" + +#: param/loadparm.c:932 +msgid "kernel oplocks" +msgstr "" + +#: param/loadparm.c:933 +msgid "locking" +msgstr "" + +#: param/loadparm.c:935 +msgid "oplocks" +msgstr "" + +#: param/loadparm.c:936 +msgid "level2 oplocks" +msgstr "" + +#: param/loadparm.c:937 +msgid "oplock break wait time" +msgstr "" + +#: param/loadparm.c:938 +msgid "oplock contention limit" +msgstr "" + +#: param/loadparm.c:939 +msgid "posix locking" +msgstr "" + +#: param/loadparm.c:940 +msgid "strict locking" +msgstr "" + +#: param/loadparm.c:941 +msgid "share modes" +msgstr "" + +#: param/loadparm.c:944 +msgid "Ldap Options" +msgstr "Opcje Ldap" + +#: param/loadparm.c:946 +msgid "ldap server" +msgstr "" + +#: param/loadparm.c:947 +msgid "ldap port" +msgstr "" + +#: param/loadparm.c:948 +msgid "ldap suffix" +msgstr "" + +#: param/loadparm.c:949 +msgid "ldap filter" +msgstr "" + +#: param/loadparm.c:950 +msgid "ldap root" +msgstr "" + +#: param/loadparm.c:951 +msgid "ldap root passwd" +msgstr "" + +#: param/loadparm.c:954 +msgid "Miscellaneous Options" +msgstr "Pozosta³e Opcje" + +#: param/loadparm.c:955 +msgid "add share command" +msgstr "" + +#: param/loadparm.c:956 +msgid "change share command" +msgstr "" + +#: param/loadparm.c:957 +#, fuzzy +msgid "delete share command" +msgstr "Usuñ Wspó³udzia³" + +#: param/loadparm.c:959 +msgid "config file" +msgstr "" + +#: param/loadparm.c:960 +msgid "preload" +msgstr "" + +#: param/loadparm.c:961 +#, fuzzy +msgid "auto services" +msgstr "Automatyczne Odĥwieżanie" + +#: param/loadparm.c:962 +msgid "lock dir" +msgstr "" + +#: param/loadparm.c:963 +msgid "lock directory" +msgstr "" + +#: param/loadparm.c:965 +msgid "utmp directory" +msgstr "" + +#: param/loadparm.c:966 +msgid "wtmp directory" +msgstr "" + +#: param/loadparm.c:967 +msgid "utmp" +msgstr "" + +#: param/loadparm.c:970 +msgid "default service" +msgstr "" + +#: param/loadparm.c:971 +#, fuzzy +msgid "default" +msgstr "Ustaw domyĥlnie" + +#: param/loadparm.c:972 +msgid "message command" +msgstr "" + +#: param/loadparm.c:973 +msgid "dfree command" +msgstr "" + +#: param/loadparm.c:974 +msgid "remote announce" +msgstr "" + +#: param/loadparm.c:975 +msgid "remote browse sync" +msgstr "" + +#: param/loadparm.c:976 +#, fuzzy +msgid "socket address" +msgstr "adres IP" + +#: param/loadparm.c:977 +msgid "homedir map" +msgstr "" + +#: param/loadparm.c:978 +msgid "time offset" +msgstr "" + +#: param/loadparm.c:979 +msgid "NIS homedir" +msgstr "" + +#: param/loadparm.c:980 +msgid "-valid" +msgstr "" + +#: param/loadparm.c:982 +msgid "copy" +msgstr "" + +#: param/loadparm.c:983 +msgid "include" +msgstr "" + +#: param/loadparm.c:984 +msgid "exec" +msgstr "" + +#: param/loadparm.c:985 +msgid "preexec" +msgstr "" + +#: param/loadparm.c:987 +msgid "preexec close" +msgstr "" + +#: param/loadparm.c:988 +msgid "postexec" +msgstr "" + +#: param/loadparm.c:989 +msgid "root preexec" +msgstr "" + +#: param/loadparm.c:990 +msgid "root preexec close" +msgstr "" + +#: param/loadparm.c:991 +msgid "root postexec" +msgstr "" + +#: param/loadparm.c:992 +msgid "available" +msgstr "" + +#: param/loadparm.c:993 +#, fuzzy +msgid "volume" +msgstr "Strona domowa" + +#: param/loadparm.c:994 +msgid "fstype" +msgstr "" + +#: param/loadparm.c:995 +msgid "set directory" +msgstr "" + +#: param/loadparm.c:996 +msgid "source environment" +msgstr "" + +#: param/loadparm.c:997 +msgid "wide links" +msgstr "" + +#: param/loadparm.c:998 +msgid "follow symlinks" +msgstr "" + +#: param/loadparm.c:999 +msgid "dont descend" +msgstr "" + +#: param/loadparm.c:1000 +msgid "magic script" +msgstr "" + +#: param/loadparm.c:1001 +msgid "magic output" +msgstr "" + +#: param/loadparm.c:1002 +msgid "delete readonly" +msgstr "" + +#: param/loadparm.c:1003 +msgid "dos filemode" +msgstr "" + +#: param/loadparm.c:1004 +msgid "dos filetimes" +msgstr "" + +#: param/loadparm.c:1005 +msgid "dos filetime resolution" +msgstr "" + +#: param/loadparm.c:1007 +msgid "fake directory create times" +msgstr "" + +#: param/loadparm.c:1008 +msgid "panic action" +msgstr "" + +#: param/loadparm.c:1009 +msgid "hide local users" +msgstr "" + +#: param/loadparm.c:1012 +#, fuzzy +msgid "VFS options" +msgstr "Opcje WINS" + +#: param/loadparm.c:1014 +msgid "vfs object" +msgstr "" + +#: param/loadparm.c:1015 +#, fuzzy +msgid "vfs options" +msgstr "Bazowe Opcje" + +#: param/loadparm.c:1018 +msgid "msdfs root" +msgstr "" + +#: param/loadparm.c:1019 +msgid "host msdfs" +msgstr "" + +#: param/loadparm.c:1021 +#, fuzzy +msgid "Winbind options" +msgstr "Opcje Drukowania" + +#: param/loadparm.c:1023 +msgid "winbind uid" +msgstr "" + +#: param/loadparm.c:1024 +msgid "winbind gid" +msgstr "" + +#: param/loadparm.c:1025 +msgid "template homedir" +msgstr "" + +#: param/loadparm.c:1026 +msgid "template shell" +msgstr "" + +#: param/loadparm.c:1027 +msgid "winbind separator" +msgstr "" + +#: param/loadparm.c:1028 +msgid "winbind cache time" +msgstr "" + +#: param/loadparm.c:1029 +msgid "winbind enum users" +msgstr "" + +#: param/loadparm.c:1030 +msgid "winbind enum groups" +msgstr "" + +#~ msgid "failed to open %s for writing\n" +#~ msgstr "nie uda³o siê otworzyĉ %s do zapisu\n" + +#~ msgid "Can't reload %s\n" +#~ msgstr "Nie mogê prze³adowaĉ %s\n" + +#~ msgid "Can't setup password database vectors.\n" +#~ msgstr "Nie można ustawiĉ wektorów bazy hase³.\n" + +#~ msgid "You need to have status=yes in your smb config file\n" +#~ msgstr "Musisz mieĉ status=yes w swoim pliku konfiguracyjnym smb\n" diff --git a/source4/po/tr.msg b/source4/po/tr.msg new file mode 100644 index 0000000000..6c2bc1f93d --- /dev/null +++ b/source4/po/tr.msg @@ -0,0 +1,1723 @@ +# Swat Turkish Translation +# Copyright (C) 2001 Deniz Akkus Kanca +# Deniz Akkus Kanca , 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. +msgid "" +msgstr "" +"Project-Id-Version: i18n_swat \n" +"POT-Creation-Date: 2001-09-20 20:29+0900\n" +"PO-Revision-Date: 2001-09-20 22:51EEST\n" +"Last-Translator: Deniz Akkus Kanca \n" +"Language-Team: Turkish \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:120 +#, c-format +msgid "ERROR: Can't open %s\n" +msgstr "HATA: %s açŭlamadŭ\n" + +#. +#. str = stripspace(parm->label); +#. strlower (str); //monyo +#. d_printf("%s       %s", +#. str, _("Help"), parm->label); +#. +#: web/swat.c:211 +msgid "Help" +msgstr "Yardŭm" + +#: web/swat.c:217 web/swat.c:231 web/swat.c:246 web/swat.c:254 web/swat.c:263 +#: web/swat.c:272 web/swat.c:278 web/swat.c:284 web/swat.c:297 +msgid "Set Default" +msgstr "Öntanŭmlŭya Ayarla" + +#: web/swat.c:502 +#, c-format +msgid "Logged in as %s

\n" +msgstr "%s kimlii ile oturum açŭlmŭŝ

\n" + +#: 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:512 +msgid "Status" +msgstr "Durum" + +#: web/swat.c:513 +msgid "View Config" +msgstr "Ayarlara Gözat" + +#: web/swat.c:515 +msgid "Password Management" +msgstr "Ŝifre Yönetimi" + +#: web/swat.c:539 +msgid "Current Config" +msgstr "Ŝimdiki Ayarlar" + +#: web/swat.c:543 +msgid "Normal View" +msgstr "Normal Görünüm" + +#: web/swat.c:545 +msgid "Full View" +msgstr "Tam Görünüm" + +#: web/swat.c:561 +msgid "Global Variables" +msgstr "Genel Deiŝkenler" + +#: web/swat.c:575 web/swat.c:671 web/swat.c:1014 +msgid "Commit Changes" +msgstr "Deiŝiklikleri Kaydet" + +#: web/swat.c:579 web/swat.c:674 web/swat.c:1016 +msgid "Reset Values" +msgstr "Deerleri Ŭlk Haline Getir" + +#: web/swat.c:581 web/swat.c:676 web/swat.c:1018 +msgid "Advanced View" +msgstr "Geliŝmiŝ Görünüm" + +#: web/swat.c:583 web/swat.c:678 web/swat.c:1020 +msgid "Basic View" +msgstr "Temel Görünüm" + +#: web/swat.c:613 +msgid "Share Parameters" +msgstr "Paylaŝŭm Parametreleri" + +#: web/swat.c:642 +msgid "Choose Share" +msgstr "Paylaŝŭm Seçin" + +#: web/swat.c:656 +msgid "Delete Share" +msgstr "Paylaŝŭm Kaldŭr" + +#: web/swat.c:663 +msgid "Create Share" +msgstr "Paylaŝŭm Oluŝtur" + +#: web/swat.c:708 +msgid "password change in demo mode rejected\n" +msgstr "demo kipinde ŝifre deiŝiklii kabul edilmedi\n" + +#: web/swat.c:747 +msgid " Must specify \"User Name\" \n" +msgstr " \"Kullanŭcŭ Adŭ\" belirtilmeli \n" + +#: web/swat.c:763 +msgid " Must specify \"Old Password\" \n" +msgstr " \"Eski Ŝifre\" belirtilmeli \n" + +#: web/swat.c:769 +msgid " Must specify \"Remote Machine\" \n" +msgstr " \"Uzak Makina\" belirtilmeli \n" + +#: web/swat.c:776 +msgid " Must specify \"New, and Re-typed Passwords\" \n" +msgstr " \"Yeni ve Tekrar Girilmiŝ Ŝifreler\" belirtilmeli \n" + +#: web/swat.c:782 +msgid " Re-typed password didn't match new password\n" +msgstr " Tekrar girilen ŝifre yeni ŝifre ile eŝleŝmedi\n" + +#: web/swat.c:812 +#, c-format +msgid " The passwd for '%s' has been changed. \n" +msgstr " '%s' için ŝifre deiŝtirildi. \n" + +#: web/swat.c:814 +#, c-format +msgid " The passwd for '%s' has NOT been changed. \n" +msgstr " '%s' için ŝifre DEŬŜTŬRŬLMEDŬ. \n" + +#: web/swat.c:838 +msgid "Server Password Management" +msgstr "Sunucu Ŝifre Yönetimi" + +#. +#. * Create all the dialog boxes for data collection +#. +#: web/swat.c:847 web/swat.c:894 +msgid " User Name : " +msgstr " Kullanŭcŭ Adŭ : " + +#: web/swat.c:850 web/swat.c:896 +msgid " Old Password : " +msgstr " Eski Ŝifre : " + +#: web/swat.c:853 web/swat.c:898 +msgid " New Password : " +msgstr " Yeni Ŝifre : " + +#: web/swat.c:855 web/swat.c:900 +msgid " Re-type New Password : " +msgstr " Yeni Ŝifre Tekrarŭ : " + +#: web/swat.c:863 web/swat.c:911 +msgid "Change Password" +msgstr "Ŝifre Deiŝtir" + +#: web/swat.c:866 +msgid "Add New User" +msgstr "Kull. Ekle" + +#: web/swat.c:868 +msgid "Delete User" +msgstr "Kull. Sil" + +#: web/swat.c:870 +msgid "Disable User" +msgstr "Kull. Etkisizleŝtir" + +#: web/swat.c:872 +msgid "Enable User" +msgstr "Kull. Etkinleŝtir" + +#: web/swat.c:885 +msgid "Client/Server Password Management" +msgstr "Ŭstemci/Sunucu Ŝifre Yönetimi" + +#: web/swat.c:902 +msgid " Remote Machine : " +msgstr " Uzak Makina : " + +#: web/swat.c:940 +msgid "Printer Parameters" +msgstr "Yazŭcŭ Bilgileri" + +#: web/swat.c:942 +msgid "Important Note:" +msgstr "Önemli Not: " + +#: web/swat.c:943 +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:944 +msgid "are autoloaded printers from " +msgstr "otomatik yüklenen yazŭcŭlar " + +#: web/swat.c:945 +msgid "Printcap Name" +msgstr "Printcap Adŭ" + +#: web/swat.c:946 +msgid "Attempting to delete these printers from SWAT will have no effect.\n" +msgstr "Bu yazŭcŭlarŭ SWAT'dan silmek etkisiz olacaktŭr.\n" + +#: web/swat.c:980 +msgid "Choose Printer" +msgstr "Yazŭcŭ Seç" + +#: web/swat.c:999 +msgid "Delete Printer" +msgstr "Yazŭcŭ Sil" + +#: web/swat.c:1006 +msgid "Create Printer" +msgstr "Yazŭcŭ Oluŝtur" + +#: web/statuspage.c:40 +msgid "DENY_NONE" +msgstr "HERKESE_AÇIK" + +#: web/statuspage.c:41 +msgid "DENY_ALL " +msgstr "HERKESŬ_REDDET " + +#: web/statuspage.c:42 +msgid "DENY_DOS " +msgstr "DOSU_REDDET " + +#: web/statuspage.c:43 +msgid "DENY_READ " +msgstr "OKU_REDDET " + +#: web/statuspage.c:44 +msgid "DENY_WRITE " +msgstr "YAZ_REDDET " + +#: web/statuspage.c:50 +msgid "RDONLY " +msgstr "SALTOKUNUR " + +#: web/statuspage.c:51 +msgid "WRONLY " +msgstr "SALTYAZILIR " + +#: web/statuspage.c:52 +msgid "RDWR " +msgstr "O/Y " + +#: web/statuspage.c:60 +msgid "EXCLUSIVE+BATCH " +msgstr "ŜAHSŬ+TOPTAN " + +#: web/statuspage.c:62 +msgid "EXCLUSIVE " +msgstr "ŜAHSŬ " + +#: web/statuspage.c:64 +msgid "BATCH " +msgstr "TOPTAN " + +#: web/statuspage.c:66 +msgid "LEVEL_II " +msgstr "SEVŬYE_II " + +#: web/statuspage.c:68 +msgid "NONE " +msgstr "HŬÇ " + +#: web/statuspage.c:195 +msgid "Server Status" +msgstr "Sunucu Durumu" + +#: web/statuspage.c:200 +msgid "Auto Refresh" +msgstr "Oto Tazele" + +#: web/statuspage.c:201 web/statuspage.c:206 +msgid "Refresh Interval: " +msgstr "Tazeleme Aralŭŭ: " + +#: web/statuspage.c:205 +msgid "Stop Refreshing" +msgstr "Tazelemeyi Durdur" + +#: web/statuspage.c:220 +msgid "version:" +msgstr "sürüm:" + +#: web/statuspage.c:223 +msgid "smbd:" +msgstr "smbd:" + +#: web/statuspage.c:223 web/statuspage.c:235 +msgid "running" +msgstr "çalŭŝŭyor" + +#: web/statuspage.c:223 web/statuspage.c:235 +msgid "not running" +msgstr "çalŭŝmŭyor" + +#: web/statuspage.c:226 +msgid "Stop smbd" +msgstr "Smbd'yi durdur" + +#: web/statuspage.c:228 +msgid "Start smbd" +msgstr "Smbd'yi çalŭŝtŭr" + +#: web/statuspage.c:230 +msgid "Restart smbd" +msgstr "Smbd'yi yeniden çalŭŝtŭr" + +#: web/statuspage.c:235 +msgid "nmbd:" +msgstr "nmbd:" + +#: web/statuspage.c:238 +msgid "Stop nmbd" +msgstr "Nmbd'yi durdur" + +#: web/statuspage.c:240 +msgid "Start nmbd" +msgstr "Nmbd'yi çalŭŝtŭr" + +#: web/statuspage.c:242 +msgid "Restart nmbd" +msgstr "Nmbd'yi yeniden çalŭŝtŭr" + +#: web/statuspage.c:249 +msgid "Active Connections" +msgstr "Aktif Balantŭlar" + +#: web/statuspage.c:251 web/statuspage.c:264 web/statuspage.c:272 +msgid "PID" +msgstr "PID" + +#: web/statuspage.c:251 web/statuspage.c:264 +msgid "Client" +msgstr "Ŭstemci" + +#: web/statuspage.c:251 +msgid "IP address" +msgstr "IP numarasŭ" + +#: web/statuspage.c:251 web/statuspage.c:264 web/statuspage.c:272 +msgid "Date" +msgstr "Tarih" + +#: web/statuspage.c:253 +msgid "Kill" +msgstr "Kapat" + +#: web/statuspage.c:261 +msgid "Active Shares" +msgstr "Aktif Paylaŝŭmlar" + +#: web/statuspage.c:264 +msgid "Share" +msgstr "Paylaŝŭm" + +#: web/statuspage.c:264 +msgid "User" +msgstr "Kullanŭcŭ" + +#: web/statuspage.c:264 +msgid "Group" +msgstr "Grup" + +#: web/statuspage.c:270 +msgid "Open Files" +msgstr "Açŭk Dosyalar" + +#: web/statuspage.c:272 +msgid "Sharing" +msgstr "Paylaŝŭlŭyor" + +#: web/statuspage.c:272 +msgid "R/W" +msgstr "O/Y" + +#: web/statuspage.c:272 +msgid "Oplock" +msgstr "Oplock" + +#: web/statuspage.c:272 +msgid "File" +msgstr "Dosya" + +#: param/loadparm.c:641 +msgid "Base Options" +msgstr "Temel Seçenekler" + +#: param/loadparm.c:643 +msgid "dos charset" +msgstr "dos karakter kümesi" + +#: param/loadparm.c:644 +msgid "unix charset" +msgstr "unix karakter kümesi" + +#: param/loadparm.c:645 +msgid "display charset" +msgstr "karakter kümesini göster" + +#: param/loadparm.c:646 +msgid "comment" +msgstr "açŭklama" + +#: param/loadparm.c:647 +msgid "path" +msgstr "yol" + +#: param/loadparm.c:648 +msgid "directory" +msgstr "dizin" + +#: param/loadparm.c:649 +msgid "workgroup" +msgstr "çalŭŝma grubu" + +#: param/loadparm.c:650 +msgid "netbios name" +msgstr "netbios adŭ" + +#: param/loadparm.c:651 +msgid "netbios aliases" +msgstr "netbios rumuzlarŭ" + +#: param/loadparm.c:652 +msgid "netbios scope" +msgstr "netbios kapsamŭ" + +#: param/loadparm.c:653 +msgid "server string" +msgstr "sunucu dizgesi" + +#: param/loadparm.c:654 +msgid "interfaces" +msgstr "arayüzler" + +#: param/loadparm.c:655 +msgid "bind interfaces only" +msgstr "yalnŭzca arayüzleri bala" + +#: param/loadparm.c:657 +msgid "Security Options" +msgstr "Güvenlik Seçenekleri" + +#: param/loadparm.c:659 +msgid "security" +msgstr "güvenlik" + +#: param/loadparm.c:660 +msgid "encrypt passwords" +msgstr "ŝifreyi ŝifrele " + +#: param/loadparm.c:661 +msgid "update encrypted" +msgstr "ŝifrelenmiŝ güncelle" + +#: param/loadparm.c:662 +msgid "allow trusted domains" +msgstr "güvenli alanlara izin ver" + +#: param/loadparm.c:663 +msgid "alternate permissions" +msgstr "baŝka izinler" + +#: param/loadparm.c:664 +msgid "hosts equiv" +msgstr "hosts eŝdeerlisi" + +#: param/loadparm.c:665 +msgid "min passwd length" +msgstr "en kŭsa ŝifre uzunluu" + +#: param/loadparm.c:666 +msgid "min password length" +msgstr "en kŭsa ŝifre uzunluu" + +#: param/loadparm.c:667 +msgid "map to guest" +msgstr "guest (misafir) kullanŭcŭya eŝle" + +#: param/loadparm.c:668 +msgid "null passwords" +msgstr "boŝ ŝifreler" + +#: param/loadparm.c:669 +msgid "obey pam restrictions" +msgstr "pam kŭsŭtlamalarŭna uy" + +#: param/loadparm.c:670 +msgid "password server" +msgstr "ŝifre sunucusu" + +#: param/loadparm.c:671 +msgid "smb passwd file" +msgstr "smb ŝifre dosyasŭ" + +#: param/loadparm.c:672 +msgid "private dir" +msgstr "özel dizin" + +#: param/loadparm.c:673 +msgid "passdb module path" +msgstr "ŝifre veritabanŭ modül yolu" + +#: param/loadparm.c:674 +msgid "root directory" +msgstr "kök dizin" + +#: param/loadparm.c:675 +msgid "root dir" +msgstr "kök dizin" + +#: param/loadparm.c:676 +msgid "root" +msgstr "kök" + +#: param/loadparm.c:678 +msgid "pam password change" +msgstr "pam ŝifre deiŝiklii" + +#: param/loadparm.c:679 +msgid "passwd program" +msgstr "ŝifre yazŭlŭmŭ" + +#: param/loadparm.c:680 +msgid "passwd chat" +msgstr "ŝifre diyalou" + +#: param/loadparm.c:681 +msgid "passwd chat debug" +msgstr "ŝifre diyalog hata ayŭklamasŭ" + +#: param/loadparm.c:682 +msgid "username map" +msgstr "kullanŭcŭ adŭ eŝlemesi" + +#: param/loadparm.c:683 +msgid "password level" +msgstr "ŝifre seviyesi" + +#: param/loadparm.c:684 +msgid "username level" +msgstr "kullanŭcŭ kimlii seviyesi" + +#: param/loadparm.c:685 +msgid "unix password sync" +msgstr "unix ŝifre senkronizasyonu" + +#: param/loadparm.c:686 +msgid "restrict anonymous" +msgstr "anonim eriŝimi kŭsŭtla" + +#: param/loadparm.c:687 +msgid "lanman auth" +msgstr "lanman auth" + +#: param/loadparm.c:688 +msgid "ntlm auth" +msgstr "ntlm auth" + +#: param/loadparm.c:689 +msgid "plaintext to smbpasswd" +msgstr "düz metinden smbpasswd'e" + +#: param/loadparm.c:690 +msgid "use rhosts" +msgstr "rhosts kullan" + +#: param/loadparm.c:692 +msgid "username" +msgstr "kullanŭcŭ adŭ" + +#: param/loadparm.c:693 +msgid "user" +msgstr "kullanŭcŭ" + +#: param/loadparm.c:694 +msgid "users" +msgstr "kullanŭcŭlar" + +#: param/loadparm.c:696 +msgid "guest account" +msgstr "misafir hesap" + +#: param/loadparm.c:697 +msgid "invalid users" +msgstr "geçersiz kullanŭcŭlar" + +#: param/loadparm.c:698 +msgid "valid users" +msgstr "geçerli kullanŭcŭlar" + +#: param/loadparm.c:699 +msgid "admin users" +msgstr "yönetici kullanŭcŭlarŭ" + +#: param/loadparm.c:700 +msgid "read list" +msgstr "okuma listesi" + +#: param/loadparm.c:701 +msgid "write list" +msgstr "yazma listesi" + +#: param/loadparm.c:702 +msgid "printer admin" +msgstr "yazŭcŭ yönetimi" + +#: param/loadparm.c:703 +msgid "force user" +msgstr "kullanŭcŭyŭ zorla" + +#: param/loadparm.c:704 +msgid "force group" +msgstr "grubu zorla" + +#: param/loadparm.c:705 +msgid "group" +msgstr "grup" + +#: param/loadparm.c:707 +msgid "read only" +msgstr "salt okunur" + +#: param/loadparm.c:708 +msgid "write ok" +msgstr "yazma tamam" + +#: param/loadparm.c:709 +msgid "writeable" +msgstr "yazŭlabilir" + +#: param/loadparm.c:710 +msgid "writable" +msgstr "yazŭlabilir" + +#: param/loadparm.c:712 +msgid "create mask" +msgstr "oluŝturma izinleri" + +#: param/loadparm.c:713 +msgid "create mode" +msgstr "oluŝturma kipi" + +#: param/loadparm.c:714 +msgid "force create mode" +msgstr "oluŝturma kipini zorla" + +#: param/loadparm.c:715 +msgid "security mask" +msgstr "güvenlik izinleri" + +#: param/loadparm.c:716 +msgid "force security mode" +msgstr "güvenlik kipini zorla" + +#: param/loadparm.c:717 +msgid "directory mask" +msgstr "dizin izinleri" + +#: param/loadparm.c:718 +msgid "directory mode" +msgstr "dizin kipi" + +#: param/loadparm.c:719 +msgid "force directory mode" +msgstr "dizin kipini zorla" + +#: param/loadparm.c:720 +msgid "directory security mask" +msgstr "dizin güvenlik izinleri" + +#: param/loadparm.c:721 +msgid "force directory security mode" +msgstr "dizin güvenlik kipini zorla" + +#: param/loadparm.c:722 +msgid "inherit permissions" +msgstr "izinleri ebeveynden al" + +#: param/loadparm.c:723 +msgid "guest only" +msgstr "yalnŭz misafir" + +#: param/loadparm.c:724 +msgid "only guest" +msgstr "yalnŭz misafir" + +#: param/loadparm.c:726 +msgid "guest ok" +msgstr "misafir tamam" + +#: param/loadparm.c:727 +msgid "public" +msgstr "genel" + +#: param/loadparm.c:729 +msgid "only user" +msgstr "salt kullanŭcŭ" + +#: param/loadparm.c:730 +msgid "hosts allow" +msgstr "hosts izinli" + +#: param/loadparm.c:731 +msgid "allow hosts" +msgstr "hosts izinli" + +#: param/loadparm.c:732 +msgid "hosts deny" +msgstr "hosts izinsiz" + +#: param/loadparm.c:733 +msgid "deny hosts" +msgstr "hosts izinsiz" + +#: param/loadparm.c:736 +msgid "Secure Socket Layer Options" +msgstr "Güvenli Soket Katman Seçenekleri" + +#: param/loadparm.c:737 +msgid "ssl" +msgstr "ssl" + +#: param/loadparm.c:739 +msgid "ssl hosts" +msgstr "ssl hosts" + +#: param/loadparm.c:740 +msgid "ssl hosts resign" +msgstr "ssl hosts istifa" + +#: param/loadparm.c:741 +msgid "ssl CA certDir" +msgstr "ssl CA sertifika dizini" + +#: param/loadparm.c:742 +msgid "ssl CA certFile" +msgstr "ssl CA sertifika dosyasŭ" + +#: param/loadparm.c:743 +msgid "ssl server cert" +msgstr "ssl sunucu sertifikasŭ" + +#: param/loadparm.c:744 +msgid "ssl server key" +msgstr "ssl sunucu anahtarŭ" + +#: param/loadparm.c:745 +msgid "ssl client cert" +msgstr "ssl istemci sertifikasŭ" + +#: param/loadparm.c:746 +msgid "ssl client key" +msgstr "ssl istemci anahtarŭ" + +#: param/loadparm.c:747 +msgid "ssl require clientcert" +msgstr "ssl istemci sertifikasŭ iste" + +#: param/loadparm.c:748 +msgid "ssl require servercert" +msgstr "ssl sunucu sertifikasŭ iste" + +#: param/loadparm.c:749 +msgid "ssl ciphers" +msgstr "ssl ŝifreleri" + +#: param/loadparm.c:750 +msgid "ssl version" +msgstr "ssl sürümü" + +#: param/loadparm.c:751 +msgid "ssl compatibility" +msgstr "ssl uyumluluu" + +#: param/loadparm.c:754 +msgid "Logging Options" +msgstr "Günlük Kaydŭ Seçenekleri" + +#: param/loadparm.c:755 +msgid "log level" +msgstr "günlük seviyesi" + +#: param/loadparm.c:756 +msgid "debuglevel" +msgstr "hata ayŭklama seviyesi" + +#: param/loadparm.c:757 +msgid "syslog" +msgstr "sistem günlüü" + +#: param/loadparm.c:758 +msgid "syslog only" +msgstr "salt sistem günlüü" + +#: param/loadparm.c:759 +msgid "log file" +msgstr "günlük dosyasŭ" + +#: param/loadparm.c:761 +msgid "max log size" +msgstr "maksimum günlük büyüklüü" + +#: param/loadparm.c:762 +msgid "timestamp logs" +msgstr "zaman damgasŭ günlükleri" + +#: param/loadparm.c:763 +msgid "debug timestamp" +msgstr "hata ayŭklama zaman damgasŭ" + +#: param/loadparm.c:764 +msgid "debug hires timestamp" +msgstr "hata ayŭklama yüksek çözünürlüklü zaman damgasŭ" + +#: param/loadparm.c:765 +msgid "debug pid" +msgstr "hata ayŭklama pid" + +#: param/loadparm.c:766 +msgid "debug uid" +msgstr "hata ayŭklama uid" + +#: param/loadparm.c:768 +msgid "Protocol Options" +msgstr "Protokol Seçenekleri" + +#: param/loadparm.c:770 +msgid "protocol" +msgstr "protokol" + +#: param/loadparm.c:771 +msgid "large readwrite" +msgstr "büyük oku/yaz" + +#: param/loadparm.c:772 +msgid "max protocol" +msgstr "max protokol" + +#: param/loadparm.c:773 +msgid "min protocol" +msgstr "min protokol" + +#: param/loadparm.c:774 +msgid "unicode" +msgstr "unicode" + +#: param/loadparm.c:775 +msgid "read bmpx" +msgstr "bmpx oku" + +#: param/loadparm.c:776 +msgid "read raw" +msgstr "ham oku" + +#: param/loadparm.c:777 +msgid "write raw" +msgstr "ham yaz" + +#: param/loadparm.c:779 +msgid "nt smb support" +msgstr "nt smb destei" + +#: param/loadparm.c:780 +msgid "nt pipe support" +msgstr "nt verihattŭ destei" + +#: param/loadparm.c:781 +msgid "nt acl support" +msgstr "nt acl destei" + +#: param/loadparm.c:782 +msgid "announce version" +msgstr "sürümü bildir" + +#: param/loadparm.c:783 +msgid "announce as" +msgstr "bildir" + +#: param/loadparm.c:784 +msgid "max mux" +msgstr "maksimum mux" + +#: param/loadparm.c:785 +msgid "max xmit" +msgstr "maksimum xmit" + +#: param/loadparm.c:787 +msgid "name resolve order" +msgstr "ad çözümleme sŭrasŭ" + +#: param/loadparm.c:788 +msgid "max packet" +msgstr "maksimum paket" + +#: param/loadparm.c:789 +msgid "packet size" +msgstr "paket büyüklüü" + +#: param/loadparm.c:790 +msgid "max ttl" +msgstr "maksimum ttl" + +#: param/loadparm.c:791 +msgid "max wins ttl" +msgstr "maksimum wins ttl" + +#: param/loadparm.c:792 +msgid "min wins ttl" +msgstr "minimum wins ttl" + +#: param/loadparm.c:793 +msgid "time server" +msgstr "zaman sunucusu" + +#: param/loadparm.c:795 +msgid "Tuning Options" +msgstr "Ayar Seçenekleri" + +#: param/loadparm.c:797 +msgid "change notify timeout" +msgstr "zamanaŝŭmŭ bildirmesini deiŝtir" + +#: param/loadparm.c:798 +msgid "deadtime" +msgstr "ölüzaman" + +#: param/loadparm.c:799 +msgid "getwd cache" +msgstr "getwd arabellei" + +#: param/loadparm.c:800 +msgid "keepalive" +msgstr "hayattatut" + +#: param/loadparm.c:802 +msgid "lpq cache time" +msgstr "lpq arabellek zamanŭ" + +#: param/loadparm.c:803 +msgid "max smbd processes" +msgstr "maksimum smbd süreci" + +#: param/loadparm.c:804 +msgid "max connections" +msgstr "maksimum balantŭ" + +#: param/loadparm.c:805 +msgid "paranoid server security" +msgstr "yüksek dereceli sunucu güvenlii" + +#: param/loadparm.c:806 +msgid "max disk size" +msgstr "maksimum disk büyüklüü" + +#: param/loadparm.c:807 +msgid "max open files" +msgstr "maksimum açŭk dosya" + +#: param/loadparm.c:808 +msgid "min print space" +msgstr "minimum yazma alanŭ" + +#: param/loadparm.c:809 +msgid "read size" +msgstr "okuma boyu" + +#: param/loadparm.c:811 +msgid "socket options" +msgstr "soket seçenekleri" + +#: param/loadparm.c:812 +msgid "stat cache size" +msgstr "durum arabellei boyu" + +#: param/loadparm.c:813 +msgid "strict allocate" +msgstr "sŭkŭ ayŭrma" + +#: param/loadparm.c:814 +msgid "strict sync" +msgstr "sŭkŭ senkronizasyon" + +#: param/loadparm.c:815 +msgid "sync always" +msgstr "herzaman senkronize" + +#: param/loadparm.c:816 +msgid "use mmap" +msgstr "bellek eŝlemesi kullan" + +#: param/loadparm.c:817 +msgid "hostname lookups" +msgstr "sunucu adŭ arama" + +#: param/loadparm.c:818 +msgid "write cache size" +msgstr "yazma arabellek boyu" + +#: param/loadparm.c:820 +msgid "Printing Options" +msgstr "Yazdŭrma Seçenekleri" + +#: param/loadparm.c:822 +msgid "total print jobs" +msgstr "toplam yazdŭrma iŝleri" + +#: param/loadparm.c:823 +msgid "max print jobs" +msgstr "maksimum yazdŭrma iŝi" + +#: param/loadparm.c:824 +msgid "load printers" +msgstr "yazŭcŭlarŭ yükle" + +#: param/loadparm.c:825 +msgid "printcap name" +msgstr "printcap adŭ" + +#: param/loadparm.c:826 +msgid "printcap" +msgstr "printcap" + +#: param/loadparm.c:827 +msgid "printable" +msgstr "yazdŭrŭlabilir" + +#: param/loadparm.c:828 +msgid "print ok" +msgstr "yazdŭrma tamam" + +#: param/loadparm.c:829 +msgid "postscript" +msgstr "postscript" + +#: param/loadparm.c:830 +msgid "printing" +msgstr "yazdŭrŭyor" + +#: param/loadparm.c:831 +msgid "print command" +msgstr "yazdŭrma komutu" + +#: param/loadparm.c:832 +msgid "disable spoolss" +msgstr "kuyruu etkisizleŝtir" + +#: param/loadparm.c:833 +msgid "lpq command" +msgstr "lpq komutu" + +#: param/loadparm.c:834 +msgid "lprm command" +msgstr "lprm komutu" + +#: param/loadparm.c:835 +msgid "lppause command" +msgstr "lppause komutu" + +#: param/loadparm.c:836 +msgid "lpresume command" +msgstr "lpresume komutu" + +#: param/loadparm.c:837 +msgid "queuepause command" +msgstr "queuepause komutu" + +#: param/loadparm.c:838 +msgid "queueresume command" +msgstr "queueresume komutu" + +#: param/loadparm.c:840 +msgid "enumports command" +msgstr "port listele komutu" + +#: param/loadparm.c:841 +msgid "addprinter command" +msgstr "yazŭcŭ ekle komutu" + +#: param/loadparm.c:842 +msgid "deleteprinter command" +msgstr "yazŭcŭ sil komutu" + +#: param/loadparm.c:843 +msgid "show add printer wizard" +msgstr "yazŭcŭ ekleme sihirbazŭnŭ göster" + +#: param/loadparm.c:844 +msgid "os2 driver map" +msgstr "os2 sürücü eŝlemesi" + +#: param/loadparm.c:846 +msgid "printer name" +msgstr "yazŭcŭ adŭ" + +#: param/loadparm.c:847 +msgid "printer" +msgstr "yazŭcŭ" + +#: param/loadparm.c:848 +msgid "use client driver" +msgstr "istemci sürücüsü kullan" + +#: param/loadparm.c:849 +msgid "printer driver" +msgstr "yazŭcŭ sürücüsü" + +#: param/loadparm.c:850 +msgid "printer driver file" +msgstr "yazŭcŭ sürücü dosyasŭ" + +#: param/loadparm.c:851 +msgid "printer driver location" +msgstr "yazŭcŭ sürücüsü yeri" + +#: param/loadparm.c:853 +msgid "Filename Handling" +msgstr "Dosyaadŭ Ŭŝlenmesi" + +#: param/loadparm.c:854 +msgid "strip dot" +msgstr "noktalarŭ bastŭr" + +#: param/loadparm.c:856 +msgid "mangled stack" +msgstr "karŭŝtŭrŭlmŭŝ yŭŭt" + +#: param/loadparm.c:857 +msgid "default case" +msgstr "öntanŭmlŭ büyük/küçük harf" + +#: param/loadparm.c:858 +msgid "case sensitive" +msgstr "büyük küçük harfe duyarlŭ" + +#: param/loadparm.c:859 +msgid "casesignames" +msgstr "casesignames" + +#: param/loadparm.c:860 +msgid "preserve case" +msgstr "büyük küçük harf ayrŭmŭnŭ tut" + +#: param/loadparm.c:861 +msgid "short preserve case" +msgstr "kŭsa büyük küçük harf ayrŭmŭnŭ tut" + +#: param/loadparm.c:862 +msgid "mangle case" +msgstr "büyük küçük harf harmanla" + +#: param/loadparm.c:863 +msgid "mangling char" +msgstr "karakter harmanlanŭyor" + +#: param/loadparm.c:864 +msgid "hide dot files" +msgstr "nokta ile baŝlayan dosyalarŭ gizle" + +#: param/loadparm.c:865 +msgid "hide unreadable" +msgstr "okunamazlarŭ sakla" + +#: param/loadparm.c:866 +msgid "delete veto files" +msgstr "veto dosyalarŭnŭ sil" + +#: param/loadparm.c:867 +msgid "veto files" +msgstr "veto dosyalarŭ" + +#: param/loadparm.c:868 +msgid "hide files" +msgstr "dosyalarŭ gizle" + +#: param/loadparm.c:869 +msgid "veto oplock files" +msgstr "veto oplock dosyalarŭ" + +#: param/loadparm.c:870 +msgid "map system" +msgstr "sistemi eŝle" + +#: param/loadparm.c:871 +msgid "map hidden" +msgstr "gizlileri eŝle" + +#: param/loadparm.c:872 +msgid "map archive" +msgstr "arŝivi eŝle" + +#: param/loadparm.c:873 +msgid "mangled names" +msgstr "harmanlanmŭŝ isimler" + +#: param/loadparm.c:874 +msgid "mangled map" +msgstr "harmanlanmŭŝ eŝleŝme" + +#: param/loadparm.c:875 +msgid "stat cache" +msgstr "durum arabellei" + +#: param/loadparm.c:877 +msgid "Domain Options" +msgstr "Alan Seçenekleri" + +#: param/loadparm.c:879 +msgid "domain admin group" +msgstr "alan yönetici grubu" + +#: param/loadparm.c:880 +msgid "domain guest group" +msgstr "alan misafir grubu" + +#: param/loadparm.c:883 +msgid "groupname map" +msgstr "grup adŭ eŝlemesi" + +#: param/loadparm.c:886 +msgid "machine password timeout" +msgstr "makina ŝifresi zamanaŝŭmŭ" + +#: param/loadparm.c:888 +msgid "Logon Options" +msgstr "Sistem Giriŝ Seçenekleri" + +#: param/loadparm.c:890 +msgid "add user script" +msgstr "kullanŭcŭ ekleme betii" + +#: param/loadparm.c:891 +msgid "delete user script" +msgstr "kullanŭcŭ silme betii" + +#: param/loadparm.c:892 +msgid "add group script" +msgstr "grup ekleme betii" + +#: param/loadparm.c:893 +msgid "delete group script" +msgstr "grup silme betii" + +#: param/loadparm.c:894 +msgid "add user to group script" +msgstr "gruba kullanŭcŭ ekleme betii" + +#: param/loadparm.c:895 +msgid "delete user from group script" +msgstr "gruptan kullanŭcŭ silme betii" + +#: param/loadparm.c:896 +msgid "add machine script" +msgstr "makina ekleme betii" + +#: param/loadparm.c:897 +msgid "shutdown script" +msgstr "sistem kapanŭŝ betii" + +#: param/loadparm.c:898 +msgid "abort shutdown script" +msgstr "sistem kapanŭŝ betiini durdur" + +#: param/loadparm.c:900 +msgid "logon script" +msgstr "sistem giriŝ betii" + +#: param/loadparm.c:901 +msgid "logon path" +msgstr "sistem giriŝ yolu" + +#: param/loadparm.c:902 +msgid "logon drive" +msgstr "sistem giriŝ aygŭtŭ" + +#: param/loadparm.c:903 +msgid "logon home" +msgstr "sistem giriŝ kökü" + +#: param/loadparm.c:904 +msgid "domain logons" +msgstr "alan giriŝleri" + +#: param/loadparm.c:906 +msgid "Browse Options" +msgstr "Gözatma Seçenekleri" + +#: param/loadparm.c:908 +msgid "os level" +msgstr "iŝletim sistem seviyesi" + +#: param/loadparm.c:909 +msgid "lm announce" +msgstr "lm bildirimi" + +#: param/loadparm.c:910 +msgid "lm interval" +msgstr "lm aralŭŭ" + +#: param/loadparm.c:911 +msgid "preferred master" +msgstr "tercih edilen ana alan sunucusu" + +#: param/loadparm.c:912 +msgid "prefered master" +msgstr "tercih edilen ana alan sunucusu" + +#: param/loadparm.c:913 +msgid "local master" +msgstr "yerel alan sunucusu" + +#: param/loadparm.c:914 +msgid "domain master" +msgstr "alan sunucusu" + +#: param/loadparm.c:915 +msgid "browse list" +msgstr "gözatma listesi" + +#: param/loadparm.c:916 +msgid "browseable" +msgstr "gözatŭlabilir" + +#: param/loadparm.c:917 +msgid "browsable" +msgstr "gözatŭlabilir" + +#: param/loadparm.c:918 +msgid "enhanced browsing" +msgstr "geliŝkin gözatma" + +#: param/loadparm.c:920 +msgid "WINS Options" +msgstr "WINS Seçenekleri" + +#: param/loadparm.c:921 +msgid "dns proxy" +msgstr "dns proxy" + +#: param/loadparm.c:922 +msgid "wins proxy" +msgstr "wins proxy" + +#: param/loadparm.c:924 +msgid "wins server" +msgstr "wins sunucusu" + +#: param/loadparm.c:925 +msgid "wins support" +msgstr "wins destei" + +#: param/loadparm.c:926 +msgid "wins hook" +msgstr "wins giriŝi" + +#: param/loadparm.c:928 +msgid "Locking Options" +msgstr "Kilitleme Seçenekleri" + +#: param/loadparm.c:930 +msgid "blocking locks" +msgstr "engelleyen kilitler" + +#: param/loadparm.c:931 +msgid "fake oplocks" +msgstr "sahte oplocklar" + +#: param/loadparm.c:932 +msgid "kernel oplocks" +msgstr "çekirdek oplocklarŭ" + +#: param/loadparm.c:933 +msgid "locking" +msgstr "kilitliyor" + +#: param/loadparm.c:935 +msgid "oplocks" +msgstr "oplocklar" + +#: param/loadparm.c:936 +msgid "level2 oplocks" +msgstr "Seviye 2 oplocklar" + +#: param/loadparm.c:937 +msgid "oplock break wait time" +msgstr "oplock kŭrma bekleme süresi" + +#: param/loadparm.c:938 +msgid "oplock contention limit" +msgstr "oplock ihtilaf limiti" + +#: param/loadparm.c:939 +msgid "posix locking" +msgstr "posix kilitlemesi" + +#: param/loadparm.c:940 +msgid "strict locking" +msgstr "sŭkŭ kilitleme" + +#: param/loadparm.c:941 +msgid "share modes" +msgstr "paylaŝŭm kipleri" + +#: param/loadparm.c:944 +msgid "Ldap Options" +msgstr "Ldap Seçenekleri" + +#: param/loadparm.c:946 +msgid "ldap server" +msgstr "ldap sunucusu" + +#: param/loadparm.c:947 +msgid "ldap port" +msgstr "ldap portu" + +#: param/loadparm.c:948 +msgid "ldap suffix" +msgstr "ldap soneki" + +#: param/loadparm.c:949 +msgid "ldap filter" +msgstr "ldap filtresi" + +#: param/loadparm.c:950 +msgid "ldap root" +msgstr "ldap kökü" + +#: param/loadparm.c:951 +msgid "ldap root passwd" +msgstr "ldap kök ŝifresi" + +#: param/loadparm.c:954 +msgid "Miscellaneous Options" +msgstr "Dier Seçenekler" + +#: param/loadparm.c:955 +msgid "add share command" +msgstr "paylaŝŭm ekle komutu" + +#: param/loadparm.c:956 +msgid "change share command" +msgstr "paylaŝŭm deiŝtir komutu" + +#: param/loadparm.c:957 +msgid "delete share command" +msgstr "paylaŝŭm sil komutu" + +#: param/loadparm.c:959 +msgid "config file" +msgstr "ayar dosyasŭ" + +#: param/loadparm.c:960 +msgid "preload" +msgstr "önyükle" + +#: param/loadparm.c:961 +msgid "auto services" +msgstr "otomatik servisler" + +#: param/loadparm.c:962 +msgid "lock dir" +msgstr "kilit dizini" + +#: param/loadparm.c:963 +msgid "lock directory" +msgstr "kilit dizini" + +#: param/loadparm.c:965 +msgid "utmp directory" +msgstr "utmp dizini" + +#: param/loadparm.c:966 +msgid "wtmp directory" +msgstr "wtmp dizini" + +#: param/loadparm.c:967 +msgid "utmp" +msgstr "utmp" + +#: param/loadparm.c:970 +msgid "default service" +msgstr "öntanŭmlŭ servis" + +#: param/loadparm.c:971 +msgid "default" +msgstr "öntanŭmlŭ" + +#: param/loadparm.c:972 +msgid "message command" +msgstr "ileti komutu" + +#: param/loadparm.c:973 +msgid "dfree command" +msgstr "dfree komutu" + +#: param/loadparm.c:974 +msgid "remote announce" +msgstr "uzak bildirim" + +#: param/loadparm.c:975 +msgid "remote browse sync" +msgstr "uzak gözatma senkronizasyonu" + +#: param/loadparm.c:976 +msgid "socket address" +msgstr "soket adresi" + +#: param/loadparm.c:977 +msgid "homedir map" +msgstr "evdizini eŝlemesi" + +#: param/loadparm.c:978 +msgid "time offset" +msgstr "zaman kaydŭrmasŭ" + +#: param/loadparm.c:979 +msgid "NIS homedir" +msgstr "NIS evdizini" + +#: param/loadparm.c:980 +msgid "-valid" +msgstr "-geçerli" + +#: param/loadparm.c:982 +msgid "copy" +msgstr "kopyala" + +#: param/loadparm.c:983 +msgid "include" +msgstr "ekle" + +#: param/loadparm.c:984 +msgid "exec" +msgstr "çalŭŝtŭr" + +#: param/loadparm.c:985 +msgid "preexec" +msgstr "preexec" + +#: param/loadparm.c:987 +msgid "preexec close" +msgstr "preexec close" + +#: param/loadparm.c:988 +msgid "postexec" +msgstr "postexec" + +#: param/loadparm.c:989 +msgid "root preexec" +msgstr "root preexec" + +#: param/loadparm.c:990 +msgid "root preexec close" +msgstr "root preexec close" + +#: param/loadparm.c:991 +msgid "root postexec" +msgstr "root postexec" + +#: param/loadparm.c:992 +msgid "available" +msgstr "mevcut" + +#: param/loadparm.c:993 +msgid "volume" +msgstr "volume" + +#: param/loadparm.c:994 +msgid "fstype" +msgstr "dosya sistem tipi" + +#: param/loadparm.c:995 +msgid "set directory" +msgstr "dizini belirle" + +#: param/loadparm.c:996 +msgid "source environment" +msgstr "source environment" + +#: param/loadparm.c:997 +msgid "wide links" +msgstr "wide links" + +#: param/loadparm.c:998 +msgid "follow symlinks" +msgstr "sembolik balarŭ izle" + +#: param/loadparm.c:999 +msgid "dont descend" +msgstr "dont descend" + +#: param/loadparm.c:1000 +msgid "magic script" +msgstr "magic script" + +#: param/loadparm.c:1001 +msgid "magic output" +msgstr "magic output" + +#: param/loadparm.c:1002 +msgid "delete readonly" +msgstr "salt okunurlarŭ sil" + +#: param/loadparm.c:1003 +msgid "dos filemode" +msgstr "dos dosya kipi" + +#: param/loadparm.c:1004 +msgid "dos filetimes" +msgstr "dos dosya zamanlarŭ" + +#: param/loadparm.c:1005 +msgid "dos filetime resolution" +msgstr "dos dosya zamanŭ çözünürlüü" + +#: param/loadparm.c:1007 +msgid "fake directory create times" +msgstr "dizin oluŝma zamanlarŭnŭ taklit et" + +#: param/loadparm.c:1008 +msgid "panic action" +msgstr "panik iŝlemi" + +#: param/loadparm.c:1009 +msgid "hide local users" +msgstr "yerel kullanŭcŭlarŭ sakla" + +#: param/loadparm.c:1012 +msgid "VFS options" +msgstr "VFS Seçenekleri" + +#: param/loadparm.c:1014 +msgid "vfs object" +msgstr "vfs nesnesi" + +#: param/loadparm.c:1015 +msgid "vfs options" +msgstr "vfs seçenekleri" + +#: param/loadparm.c:1018 +msgid "msdfs root" +msgstr "msdfs kökü" + +#: param/loadparm.c:1019 +msgid "host msdfs" +msgstr "host msdfs" + +#: param/loadparm.c:1021 +msgid "Winbind options" +msgstr "Winbind seçenekleri" + +#: param/loadparm.c:1023 +msgid "winbind uid" +msgstr "winbind uid" + +#: param/loadparm.c:1024 +msgid "winbind gid" +msgstr "winbind gid" + +#: param/loadparm.c:1025 +msgid "template homedir" +msgstr "örnek ev dizini" + +#: param/loadparm.c:1026 +msgid "template shell" +msgstr "örnek kabuk" + +#: param/loadparm.c:1027 +msgid "winbind separator" +msgstr "winbind ayracŭ" + +#: param/loadparm.c:1028 +msgid "winbind cache time" +msgstr "winbind arabellek zamanŭ" + +#: param/loadparm.c:1029 +msgid "winbind enum users" +msgstr "winbind kullanŭcŭ listele" + +#: param/loadparm.c:1030 +msgid "winbind enum groups" +msgstr "winbind grup listele" + diff --git a/source4/popt/.cvsignore b/source4/popt/.cvsignore new file mode 100644 index 0000000000..86b08b58d2 --- /dev/null +++ b/source4/popt/.cvsignore @@ -0,0 +1,8 @@ +ID +Makefile +config.cache +config.h +config.log +config.status +rsync +zlib/dummy diff --git a/source4/popt/CHANGES b/source4/popt/CHANGES new file mode 100644 index 0000000000..b6ab2aa308 --- /dev/null +++ b/source4/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/source4/popt/COPYING b/source4/popt/COPYING new file mode 100644 index 0000000000..b4c7ca876c --- /dev/null +++ b/source4/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/source4/popt/README b/source4/popt/README new file mode 100644 index 0000000000..7fccc836ff --- /dev/null +++ b/source4/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/source4/popt/dummy.in b/source4/popt/dummy.in new file mode 100644 index 0000000000..e69de29bb2 diff --git a/source4/popt/findme.c b/source4/popt/findme.c new file mode 100644 index 0000000000..f2ad05bb3f --- /dev/null +++ b/source4/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/source4/popt/findme.h b/source4/popt/findme.h new file mode 100644 index 0000000000..5e93963d60 --- /dev/null +++ b/source4/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/source4/popt/popt.c b/source4/popt/popt.c new file mode 100644 index 0000000000..9fa8650312 --- /dev/null +++ b/source4/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 + * XXX from Norbert Warmuth + */ +#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; iarg_strip)) { + numargs--; + } + } + + for(i=1; iarg_strip)) { + continue; + } else { + if(j /* 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/source4/popt/poptconfig.c b/source4/popt/poptconfig.c new file mode 100644 index 0000000000..eb76941363 --- /dev/null +++ b/source4/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/source4/popt/popthelp.c b/source4/popt/popthelp.c new file mode 100644 index 0000000000..6b790a63e7 --- /dev/null +++ b/source4/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/source4/popt/poptint.h b/source4/popt/poptint.h new file mode 100644 index 0000000000..1847ffafe6 --- /dev/null +++ b/source4/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/source4/popt/poptparse.c b/source4/popt/poptparse.c new file mode 100644 index 0000000000..8f00769be9 --- /dev/null +++ b/source4/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/source4/popt/system.h b/source4/popt/system.h new file mode 100644 index 0000000000..059c045817 --- /dev/null +++ b/source4/popt/system.h @@ -0,0 +1,53 @@ +#include "config.h" + +#include +#include +#include +#include + +#if HAVE_MCHECK_H +#include +#endif + +#include +#include +#include + +#if HAVE_UNISTD_H +#include +#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 +#endif + +/* AIX requires this to be the first thing in the file. */ +#ifndef __GNUC__ +# if HAVE_ALLOCA_H +# include +# 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/source4/printing/.cvsignore b/source4/printing/.cvsignore new file mode 100644 index 0000000000..e69de29bb2 diff --git a/source4/printing/load.c b/source4/printing/load.c new file mode 100644 index 0000000000..cd90cbb6f3 --- /dev/null +++ b/source4/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/source4/printing/lpq_parse.c b/source4/printing/lpq_parse.c new file mode 100644 index 0000000000..4b91b8ac9a --- /dev/null +++ b/source4/printing/lpq_parse.c @@ -0,0 +1,1099 @@ +/* + 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 + + + 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); + StrnCpy(buf->fs_user,tok[USERTOK],sizeof(buf->fs_user)-1); + StrnCpy(buf->fs_file,tok[FILETOK],sizeof(buf->fs_file)-1); + + 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); +} + +/* + +LPRng_time modifies the current date by inserting the hour and minute from +the lpq output. The lpq time looks like "23:15:07" + + June 30, 1998. +Modified to work with the re-written parse_lpq_lprng routine. + + 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 + 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]); + + StrnCpy(buf->fs_user,tokarr[LPRNG_USERTOK],sizeof(buf->fs_user)-1); + + /* 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'; + } + + StrnCpy(buf->fs_file,tokarr[LPRNG_FILETOK],sizeof(buf->fs_file)-1); + + 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); + StrnCpy(buf->fs_user,tok[3],sizeof(buf->fs_user)-1); + StrnCpy(buf->fs_file,tok[2],sizeof(buf->fs_file)-1); + } + 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); + StrnCpy(buf->fs_user,tok[5],sizeof(buf->fs_user)-1); + StrnCpy(buf->fs_file,tok[4],sizeof(buf->fs_file)-1); + } + + + 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]); + StrnCpy(buf->fs_file,tok[0],sizeof(buf->fs_file)-1); + + /* fill things from header line */ + buf->time = jobtime; + buf->job = jobid; + buf->status = jobstat; + buf->priority = jobprio; + StrnCpy(buf->fs_user,jobuser,sizeof(buf->fs_user)-1); + + 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]); + StrnCpy(jobuser,tok[2],sizeof(buf->fs_user)-1); + 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); + StrnCpy(buf->fs_user,tok[2],sizeof(buf->fs_user)-1); + StrnCpy(buf->fs_file,tok[2],sizeof(buf->fs_file)-1); + 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; + + 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); + StrnCpy(buf->fs_user,tok[1],sizeof(buf->fs_user)-1); + StrnCpy(buf->fs_file,tok[6],sizeof(buf->fs_file)-1); + return(True); +} + + +/**************************************************************************** + parse a lpq line for the plp printing system + Bertrand Wallrich + +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); + StrnCpy(buf->fs_user,tok[1],sizeof(buf->fs_user)-1); + StrnCpy(buf->fs_file,tok[6],sizeof(buf->fs_file)-1); + return(True); +} + +/**************************************************************************** +parse a qstat line + +here is an example of "qstat -l -d qms" output under softq + +Queue qms: 2 jobs; daemon active (313); enabled; accepting; + job-ID submission-time pri size owner title +205980: H 98/03/09 13:04:05 0 15733 stephenf chap1.ps +206086:> 98/03/12 17:24:40 0 659 chris - +206087: 98/03/12 17:24:45 0 4876 chris - +Total: 21268 bytes in queue + + +****************************************************************************/ +static BOOL parse_lpq_softq(char *line,print_queue_struct *buf,BOOL first) +{ + fstring tok[10]; + int count=0; + const char *cline = line; + + /* mung all the ":"s to spaces*/ + string_sub(line,":"," ",0); + + for (count=0; count<10 && next_token(&cline,tok[count],NULL,sizeof(tok[count])); count++) ; + + /* we must get 9 tokens */ + if (count < 9) + return(False); + + /* the 1st and 7th columns must be integer */ + if (!isdigit((int)*tok[0]) || !isdigit((int)*tok[6])) return(False); + /* if the 2nd column is either '>' or 'H' then the 7th and 8th must be + * integer, else it's the 6th and 7th that must be + */ + if (*tok[1] == 'H' || *tok[1] == '>') + { + if (!isdigit((int)*tok[7])) + return(False); + buf->status = *tok[1] == '>' ? LPQ_PRINTING : LPQ_PAUSED; + count = 1; + } + else + { + if (!isdigit((int)*tok[5])) + return(False); + buf->status = LPQ_QUEUED; + count = 0; + } + + + buf->job = atoi(tok[0]); + buf->size = atoi(tok[count+6]); + buf->priority = atoi(tok[count+5]); + StrnCpy(buf->fs_user,tok[count+7],sizeof(buf->fs_user)-1); + StrnCpy(buf->fs_file,tok[count+8],sizeof(buf->fs_file)-1); + buf->time = time(NULL); /* default case: take current time */ + { + time_t jobtime; + struct tm *t; + + t = localtime(&buf->time); + t->tm_mday = atoi(tok[count+2]+6); + t->tm_mon = atoi(tok[count+2]+3); + switch (*tok[count+2]) + { + case 7: case 8: case 9: t->tm_year = atoi(tok[count+2]); break; + default: t->tm_year = atoi(tok[count+2]); break; + } + + t->tm_hour = atoi(tok[count+3]); + t->tm_min = atoi(tok[count+4]); + t->tm_sec = atoi(tok[count+5]); + jobtime = mktime(t); + if (jobtime != (time_t)-1) + buf->time = jobtime; + } + + 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_string(parse_line.status, NULL, " "); + 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_string(parse_line.jobname, NULL, " "); + + buf->job = atoi(parse_line.jobid); + buf->priority = 0; + buf->size = atoi(parse_line.size); + buf->time = time(NULL); + StrnCpy(buf->fs_user, parse_line.owner, sizeof(buf->fs_user)-1); + StrnCpy(buf->fs_file, parse_line.jobname, sizeof(buf->fs_file)-1); + 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_string(parse_line.jobname, NULL, " "); + StrnCpy(buf->fs_file, parse_line.jobname, sizeof(buf->fs_file)-1); + + 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_string(parse_line.status, NULL, " "); + if (!strequal(parse_line.status, LPROS2_PRINTING) && + !strequal(parse_line.status, LPROS2_PAUSED) && + !strequal(parse_line.status, LPROS2_WAITING)) + return(False); + + StrnCpy(buf->fs_user, parse_line.owner, sizeof(buf->fs_user)-1); + 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_SOFTQ: + ret = parse_lpq_softq(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(line); + + switch (status->status) { + case LPSTAT_OK: + for (i=0; stat0_strings[i]; i++) + if (strstr(line,stat0_strings[i])) { + StrnCpy(status->message,line,sizeof(status->message)-1); + status->status=LPSTAT_OK; + return ret; + } + case LPSTAT_STOPPED: + for (i=0; stat1_strings[i]; i++) + if (strstr(line,stat1_strings[i])) { + StrnCpy(status->message,line,sizeof(status->message)-1); + status->status=LPSTAT_STOPPED; + return ret; + } + case LPSTAT_ERROR: + for (i=0; stat2_strings[i]; i++) + if (strstr(line,stat2_strings[i])) { + StrnCpy(status->message,line,sizeof(status->message)-1); + status->status=LPSTAT_ERROR; + return ret; + } + break; + } + } + + return(ret); +} diff --git a/source4/printing/notify.c b/source4/printing/notify.c new file mode 100644 index 0000000000..9e92e5daa6 --- /dev/null +++ b/source4/printing/notify.c @@ -0,0 +1,526 @@ +/* + Unix SMB/Netbios implementation. + Version 2.2 + 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 "printing.h" + +static TALLOC_CTX *send_ctx; + +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, "ddddd", + 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); + 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); + 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 %d print notify message%s to printer %s\n", + 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++) + 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); +} + +/********************************************************************** + 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 message unique to each name/type/field/id/flags + * tuple. There is no point in sending multiple messages that match + * as they will just cause flickering updates in the client. + */ + + 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 [%d] failed!\n", + sizeof(SPOOLSS_NOTIFY_MSG))); + return; + } + copy_notify2_msg(pnqueue->msg, msg); + 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); +} + +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( 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/source4/printing/nt_printing.c b/source4/printing/nt_printing.c new file mode 100644 index 0000000000..017dade404 --- /dev/null +++ b/source4/printing/nt_printing.c @@ -0,0 +1,4887 @@ +/* + * 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} +}; + +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 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; nflags; + (*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; + fstring short_archi; + fstring *fl; + pstring key; + TDB_DATA kbuf, newkey; + + get_short_archi(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. +****************************************************************************/ +BOOL get_short_archi(char *short_archi, const char *long_archi) +{ + struct table { + const char *long_archi; + const char *short_archi; + }; + + struct table archi_table[]= + { + {"Windows 4.0", "WIN40" }, + {"Windows NT x86", "W32X86" }, + {"Windows NT R4000", "W32MIPS" }, + {"Windows NT Alpha_AXP", "W32ALPHA" }, + {"Windows NT PowerPC", "W32PPC" }, + {NULL, "" } + }; + + int i=-1; + + DEBUG(107,("Getting architecture dependant directory\n")); + + if (long_archi == NULL) { + DEBUGADD(107,("Bad long_archi param.!\n")); + return False; + } + + 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(107,("Unknown architecture [%s] !\n", long_archi)); + return False; + } + + StrnCpy (short_archi, archi_table[i].short_archi, strlen(archi_table[i].short_archi)); + + DEBUGADD(108,("index: [%d]\n", i)); + DEBUGADD(108,("long architecture: [%s]\n", long_archi)); + DEBUGADD(108,("short architecture: [%s]\n", short_archi)); + + return True; +} + +/**************************************************************************** + 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; + 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 = %d\n", + fname, 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 (fsp->conn->vfs_ops.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 = %d\n", + fname, 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) { + int num_sections; + 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; + 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 = %d\n", + fname, 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) { + int section_pos = IVAL(buf,sec_offset+PE_HEADER_SECT_PTR_DATA_OFFSET); + int section_bytes = IVAL(buf,sec_offset+PE_HEADER_SECT_SIZE_DATA_OFFSET); + + 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 (fsp->conn->vfs_ops.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 = %d\n", + fname, byte_count)); + goto error_exit; + } + + for (i=0; i>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; ibyte_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_countconn->vfs_ops.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(struct tcon_context *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), + 0, 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 (fsp->conn->vfs_ops.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), + 0, 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 (fsp->conn->vfs_ops.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(fstring 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; + files_struct *fsp = NULL; + BOOL bad_path; + SMB_STRUCT_STAT st; + struct tcon_context *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); + become_root(); + conn = make_connection_with_chdir("print$", null_pw, "A:", 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, conn->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), + 0, 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) +{ + fstring 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); + } + } + } + + get_short_archi(architecture, 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) +{ + fstring 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); + } + } + } + + get_short_archi(architecture, 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; + fstring architecture; + pstring new_dir; + pstring old_name; + pstring new_name; + DATA_BLOB null_pw; + struct tcon_context *conn; + NTSTATUS nt_status; + pstring inbuf; + pstring outbuf; + 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; + } + + get_short_archi(architecture, 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. + */ + + become_root(); + null_pw = data_blob(NULL, 0); + conn = make_connection_with_chdir("print$", null_pw, "A:", 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, 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, 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, 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, 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, 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; + fstring architecture; + pstring directory; + fstring temp_name; + pstring key; + char *buf; + int i, ret; + TDB_DATA kbuf, dbuf; + + get_short_archi(architecture, 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 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; + fstring architecture; + int len = 0; + int i; + pstring key; + + ZERO_STRUCT(driver); + + get_short_archi(architecture, arch); + + 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; inum_keys; i++ ) { + val_ctr = &data->keys[i].values; + num_values = regval_ctr_numvals( val_ctr ); + + /* loop over all values */ + + for ( j=0; j\ */ + + 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_string(info->printername, "\\", NULL); + 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[32]; + 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)); + 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; inum_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; inum_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; inum_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; jdata, 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, lp_netbios_name()); + + get_myfullname(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; +} + +#ifdef HAVE_ADS +static void store_printer_guid(NT_PRINTER_INFO_LEVEL_2 *info2, GUID 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(GUID)); +} + +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; + void *res = NULL; + ADS_STRUCT *ads; + const char *attrs[] = {"objectGUID", NULL}; + GUID 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); + + /* connect to the ADS server */ + ads = ads_init(NULL, NULL, lp_ads_server()); + if (!ads) { + DEBUG(3, ("ads_init() failed\n")); + return WERR_SERVER_UNAVAILABLE; + } + 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, lp_netbios_name()); + srv_dn = ldap_get_dn(ads->ld, res); + ads_msgfree(ads, res); + srv_cn = ldap_explode_dn(srv_dn, 1); + asprintf(&prt_dn, "cn=%s-%s,%s", srv_cn[0], + printer->info_2->sharename, srv_dn); + ads_memfree(ads, srv_dn); + + /* 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, lp_ads_server()); + if (!ads) { + DEBUG(3, ("ads_init() failed\n")); + return WERR_SERVER_UNAVAILABLE; + } + 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, lp_netbios_name()); + 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, GUID *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(GUID)) + memcpy(guid, regval_data_p(guid_val), sizeof(GUID)); + + 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, GUID *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; inum_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; inum_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; inum_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; inum_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; + + 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, 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", ®val_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, 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; + + 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); + } + + 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; inum_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; jdevmode = dup_nt_devicemode( printer->devmode ); + + ZERO_STRUCT( copy->data ); + copy_printer_data( ©->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. +****************************************************************************/ + +#define ENABLE_PRINT_HND_CACHE 1 + +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 + */ +#ifdef ENABLE_PRINT_HND_CACHE /* JERRY */ + if ( print_hnd + && (print_hnd->printer_type==PRINTER_HANDLE_IS_PRINTER) + && print_hnd->printer_info ) + { + if ( !(printer->info_2 = dup_printer_2(print_hnd->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; + } +#endif + + /* no cache; look it up on disk */ + + result=get_a_printer_2(&printer->info_2, sharename); + if (W_ERROR_IS_OK(result)) { + dump_a_printer(*printer, level); + +#if ENABLE_PRINT_HND_CACHE /* JERRY */ + /* 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 ) { + 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")); + } + + } +#endif + *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; snumname, 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; ienvironment, 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; + struct tcon_context *conn; + DATA_BLOB null_pw; + NTSTATUS nt_status; + + 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. + */ + + become_root(); + null_pw = data_blob( NULL, 0 ); + conn = make_connection_with_chdir( "print$", null_pw, "A:", 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; + fstring arch; + TDB_DATA kbuf, dbuf; + NT_PRINTER_DRIVER_INFO_LEVEL ctr; + + /* delete the tdb data first */ + + get_short_archi(arch, 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, + 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, + &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, + &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 = ¤t_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/source4/printing/pcap.c b/source4/printing/pcap.c new file mode 100644 index 0000000000..c399c3c6cc --- /dev/null +++ b/source4/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 : */ + 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 : */ + 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); + 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); + 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) + { + StrnCpy(comment,p,sizeof(comment)-1); + 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)) + { + StrnCpy(comment,p,sizeof(comment)-1); + continue; + } + } + + comment[60] = 0; + name[MAXPRINTERLEN] = 0; + + if (*name) + fn(name,comment); + } + x_fclose(pfile); +} diff --git a/source4/printing/print_cups.c b/source4/printing/print_cups.c new file mode 100644 index 0000000000..7cf21c966e --- /dev/null +++ b/source4/printing/print_cups.c @@ -0,0 +1,1293 @@ +/* + * 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 "printing.h" + +#ifdef HAVE_CUPS +#include +#include + + +/* + * 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 */ + + + 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); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, + "job-originating-host-name", NULL, + get_remote_machine_name()); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL, + pjob->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/source4/printing/print_generic.c b/source4/printing/print_generic.c new file mode 100644 index 0000000000..4d77b827bf --- /dev/null +++ b/source4/printing/print_generic.c @@ -0,0 +1,245 @@ +/* + 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 "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, "%d", 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) { + for (i=0; iname = 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/source4/printing/printfsp.c b/source4/printing/printfsp.c new file mode 100644 index 0000000000..5d230f8be4 --- /dev/null +++ b/source4/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(struct tcon_context *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(¤t_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; + conn->vfs_ops.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/source4/printing/printing.c b/source4/printing/printing.c new file mode 100644 index 0000000000..26ea52e35a --- /dev/null +++ b/source4/printing/printing.c @@ -0,0 +1,2128 @@ +/* + 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 "printing.h" + +/* Current printer interface */ +static struct printif *current_printif = &generic_printif; + +/* + 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)) { + memcpy(&rap_jobid, data.dptr, sizeof(uint16)); + 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 )); + + memcpy(&rap_jobid, data.dptr, sizeof(uint16)); + 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. */ + + /* select the appropriate printing interface... */ +#ifdef HAVE_CUPS + if (strcmp(lp_printcapname(), "cups") == 0) + current_printif = &cups_printif; +#endif /* HAVE_CUPS */ + + /* 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. */ +} + +/**************************************************************************** + 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; + + if ( !buf || !pjob ) + return -1; + + len += tdb_unpack(buf+len, buflen-len, "dddddddddffff", + &pjob->pid, + &pjob->sysjob, + &pjob->fd, + &pjob->starttime, + &pjob->status, + &pjob->size, + &pjob->page_count, + &pjob->spooled, + &pjob->smbjob, + 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; + + 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", + pjob->pid, + pjob->sysjob, + pjob->fd, + pjob->starttime, + pjob->status, + pjob->size, + pjob->page_count, + pjob->spooled, + 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 && (old_data.dsize == 0 || old_data.dsize == sizeof(*pjob)) ) + pjob_store_notify( snum, jobid, (struct printjob *)old_data.dptr, 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; + pj.smbjob = (old_pj != NULL ? True : False); + fstrcpy(pj.filename, old_pj ? old_pj->filename : ""); + if (jobid < UNIX_JOB_START) + fstrcpy(pj.jobname, old_pj ? old_pj->jobname : "Remote Downlevel Document"); + else + 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; + + if ( key.dsize != sizeof(jobid) ) + return 0; + + memcpy(&jobid, key.dptr, sizeof(jobid)); + 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;iqcount;i++) { + uint32 u_jobid = (ts->queue[i].job + UNIX_JOB_START); + if (jobid == u_jobid) + break; + } + if (i == ts->qcount) + pjob_delete(ts->snum, jobid); + else + ts->total_jobs++; + return 0; + } + + /* 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)) + pjob_delete(ts->snum, jobid); + else + ts->total_jobs++; + return 0; + } + + for (i=0;iqcount;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) + pjob_delete(ts->snum, jobid); + else + ts->total_jobs++; + } + else + 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; + } + + memcpy(&updating_pid, data.dptr, sizeof(pid_t)); + 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(); + 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; + } + + data.dptr = (void *)&updating_pid; + data.dsize = sizeof(pid_t); + + tdb_store(pdb->tdb, key, data, TDB_REPLACE); + release_print_db(pdb); +} + +/**************************************************************************** + 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; + struct tdb_print_db *pdb; + + 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)); + + /* + 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 + */ + for (i=0; isysjob = queue[i].job; + pjob->status = queue[i].status; + + pjob_store(snum, jobid, pjob); + } + + /* 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); + + 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; idxtdb; + } + + 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_by_string(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; idxtdb; + } + + 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_by_string(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); +} + +/**************************************************************************** + 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; + + 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); + + /* Delete the tdb entry if the delete suceeded or the job hasn't + been spooled. */ + + if (result == 0) + pjob_delete(snum, jobid); + + 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; + + 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; + + 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)) + 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; +} + +/*************************************************************************** + 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); + + /* 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; + + 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; + } + + 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); + return False; +} + +/**************************************************************************** + Utility fn to enumerate the print queue. +****************************************************************************/ + +static int traverse_fn_queue(TDB_CONTEXT *t, TDB_DATA key, TDB_DATA data, void *state) +{ + struct traverse_struct *ts = (struct traverse_struct *)state; + struct printjob pjob; + int i; + uint32 jobid; + + /* sanity checks */ + + if ( key.dsize != sizeof(jobid) ) + return 0; + + memcpy(&jobid, key.dptr, sizeof(jobid)); + + if ( unpack_pjob( data.dptr, data.dsize, &pjob ) == -1 ) + return 0; + free_nt_devicemode( &pjob.nt_devmode ); + + /* maybe it isn't for this queue */ + if (ts->snum != lp_servicenumber(pjob.queuename)) + return 0; + + if (ts->qcount >= ts->maxcount) + return 0; + + i = ts->qcount; + + 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->qcount++; + + return 0; +} + +struct traverse_count_struct { + int snum, count; +}; + +/**************************************************************************** + Utility fn to count the number of entries in the print queue. +****************************************************************************/ + +static int traverse_count_fn_queue(TDB_CONTEXT *t, TDB_DATA key, TDB_DATA data, void *state) +{ + struct traverse_count_struct *ts = (struct traverse_count_struct *)state; + struct printjob pjob; + uint32 jobid; + + /* sanity checks */ + + if ( key.dsize != sizeof(jobid) ) + return 0; + + memcpy(&jobid, key.dptr, sizeof(jobid)); + + if ( unpack_pjob( data.dptr, data.dsize, &pjob ) == -1 ) + return 0; + + free_nt_devicemode( &pjob.nt_devmode ); + + /* maybe it isn't for this queue - this cannot happen with the tdb/printer code. JRA */ + if (ts->snum != lp_servicenumber(pjob.queuename)) + return 0; + + ts->count++; + + return 0; +} + +/**************************************************************************** + 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; +} + +/**************************************************************************** + 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 **queue, + print_status_struct *status) +{ + struct traverse_struct tstruct; + struct traverse_count_struct tsc; + fstring keystr; + TDB_DATA data, key; + const char *printername; + struct tdb_print_db *pdb; + + /* make sure the database is up to date */ + + if (print_cache_expired(snum)) + print_queue_update(snum); + + /* return if we are done */ + + if ( !queue || !status ) + return 0; + + *queue = 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)) { + 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. + */ + tsc.count = 0; + tsc.snum = snum; + + tdb_traverse(pdb->tdb, traverse_count_fn_queue, (void *)&tsc); + + if (tsc.count == 0) { + release_print_db(pdb); + return 0; + } + + /* Allocate the queue size. */ + if ((tstruct.queue = (print_queue_struct *) + malloc(sizeof(print_queue_struct)*tsc.count)) == NULL) { + release_print_db(pdb); + return 0; + } + + /* + * Fill in the queue. + * We need maxcount as the queue size may have changed between + * the two calls to tdb_traverse. + */ + tstruct.qcount = 0; + tstruct.maxcount = tsc.count; + tstruct.snum = snum; + + tdb_traverse(pdb->tdb, traverse_fn_queue, (void *)&tstruct); + release_print_db(pdb); + + /* Sort the queue by submission time otherwise they are displayed + in hash order. */ + + qsort(tstruct.queue, tstruct.qcount, sizeof(print_queue_struct), + QSORT_CAST(printjob_comp)); + + *queue = tstruct.queue; + return tstruct.qcount; +} + +/**************************************************************************** + Pause a queue. +****************************************************************************/ + +BOOL print_queue_pause(struct current_user *user, int snum, WERROR *errcode) +{ + int ret; + + 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; + + 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;inext) { + /* 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_log(printdb_path, 5000, TDB_DEFAULT, O_RDWR|O_CREAT, 0600); + + 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_by_string( 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_by_string(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/source4/python/.cvsignore b/source4/python/.cvsignore new file mode 100644 index 0000000000..7e99e367f8 --- /dev/null +++ b/source4/python/.cvsignore @@ -0,0 +1 @@ +*.pyc \ No newline at end of file diff --git a/source4/python/README b/source4/python/README new file mode 100644 index 0000000000..04f794215a --- /dev/null +++ b/source4/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/source4/python/examples/spoolss/changeid.py b/source4/python/examples/spoolss/changeid.py new file mode 100755 index 0000000000..85fe0efe8a --- /dev/null +++ b/source4/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 " + 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/source4/python/examples/spoolss/enumprinters.py b/source4/python/examples/spoolss/enumprinters.py new file mode 100755 index 0000000000..478c46bc24 --- /dev/null +++ b/source4/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 [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/source4/python/examples/spoolss/psec.py b/source4/python/examples/spoolss/psec.py new file mode 100755 index 0000000000..498a0ef174 --- /dev/null +++ b/source4/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/source4/python/examples/tdbpack/.cvsignore b/source4/python/examples/tdbpack/.cvsignore new file mode 100644 index 0000000000..52e4e61140 --- /dev/null +++ b/source4/python/examples/tdbpack/.cvsignore @@ -0,0 +1,2 @@ +*.pyc +*.pyo diff --git a/source4/python/examples/tdbpack/oldtdbutil.py b/source4/python/examples/tdbpack/oldtdbutil.py new file mode 100644 index 0000000000..ac435b8bac --- /dev/null +++ b/source4/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/source4/python/examples/tdbpack/tdbtimetrial.py b/source4/python/examples/tdbpack/tdbtimetrial.py new file mode 100755 index 0000000000..be6404899d --- /dev/null +++ b/source4/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/source4/python/examples/tdbpack/test_tdbpack.py b/source4/python/examples/tdbpack/test_tdbpack.py new file mode 100755 index 0000000000..837600f789 --- /dev/null +++ b/source4/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 ' + +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/source4/python/gprinterdata b/source4/python/gprinterdata new file mode 100755 index 0000000000..cd062076c0 --- /dev/null +++ b/source4/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] " + print "where 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/source4/python/gtdbtool b/source4/python/gtdbtool new file mode 100755 index 0000000000..129f4fe0e2 --- /dev/null +++ b/source4/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 " + 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/source4/python/gtkdictbrowser.py b/source4/python/gtkdictbrowser.py new file mode 100755 index 0000000000..dd8bed8f47 --- /dev/null +++ b/source4/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/source4/python/mkpatch b/source4/python/mkpatch new file mode 100755 index 0000000000..ab5be1b6a2 --- /dev/null +++ b/source4/python/mkpatch @@ -0,0 +1,6 @@ +#!/bin/sh +# +# Make samba-head.patch. Must be run from samba source directory. +# + +cvs -z3 diff -u Makefile.in configure.in > python/samba-head.patch diff --git a/source4/python/py_common.c b/source4/python/py_common.c new file mode 100644 index 0000000000..ea092d9370 --- /dev/null +++ b/source4/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, 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/source4/python/py_common.h b/source4/python/py_common.h new file mode 100644 index 0000000000..2bbd148ff4 --- /dev/null +++ b/source4/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/source4/python/py_conv.c b/source4/python/py_conv.c new file mode 100644 index 0000000000..d0a2d78aab --- /dev/null +++ b/source4/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/source4/python/py_conv.h b/source4/python/py_conv.h new file mode 100644 index 0000000000..798661c3a0 --- /dev/null +++ b/source4/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/source4/python/py_lsa.c b/source4/python/py_lsa.c new file mode 100644 index 0000000000..22db29665a --- /dev/null +++ b/source4/python/py_lsa.c @@ -0,0 +1,468 @@ +/* + 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); + + if (mem_ctx) + 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; + NTSTATUS ntstatus; + lsa_policy_hnd_object *hnd = (lsa_policy_hnd_object *)self; + int num_names, i; + const char **names; + DOM_SID *sids; + 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 (PyList_Check(py_names)) { + + /* Convert list to char ** array */ + + num_names = PyList_Size(py_names); + names = (const char **)talloc( + hnd->mem_ctx, num_names * sizeof(char *)); + + for (i = 0; i < num_names; i++) { + PyObject *obj = PyList_GetItem(py_names, i); + + names[i] = talloc_strdup(hnd->mem_ctx, PyString_AsString(obj)); + } + + } else { + + /* Just a single element */ + + num_names = 1; + names = (const char **)talloc(hnd->mem_ctx, sizeof(char *)); + + names[0] = PyString_AsString(py_names); + } + + ntstatus = cli_lsa_lookup_names(hnd->cli, hnd->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)); + return NULL; + } + + 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("(Oi)", sid_obj, name_types[i]); + + PyList_SetItem(result, i, obj); + } + + return result; +} + +static PyObject *lsa_lookup_sids(PyObject *self, PyObject *args, + PyObject *kw) +{ + PyObject *py_sids, *result; + NTSTATUS ntstatus; + int num_sids, i; + char **domains, **names; + uint32 *types; + lsa_policy_hnd_object *hnd = (lsa_policy_hnd_object *)self; + 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 (PyList_Check(py_sids)) { + + /* Convert dictionary to char ** array */ + + num_sids = PyList_Size(py_sids); + sids = (DOM_SID *)talloc(hnd->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"); + return NULL; + } + } + + } else { + + /* Just a single element */ + + num_sids = 1; + sids = (DOM_SID *)talloc(hnd->mem_ctx, sizeof(DOM_SID)); + + if (!string_to_sid(&sids[0], PyString_AsString(py_sids))) { + PyErr_SetString(PyExc_ValueError, "string_to_sid failed"); + return NULL; + } + } + + ntstatus = cli_lsa_lookup_sids(hnd->cli, hnd->mem_ctx, &hnd->pol, + num_sids, sids, &domains, &names, + &types); + + if (!NT_STATUS_IS_OK(ntstatus)) { + PyErr_SetObject(lsa_ntstatus, py_ntstatus_tuple(ntstatus)); + return NULL; + } + + 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); + } + + 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. + +Initialises Samba's debug logging system. One argument is expected which +is a boolean specifying whether debugging is interactive and sent to stdout +or logged to a file. + +Example: + +>>> spoolss.setup_logging(interactive = 1)" }, + + { "get_debuglevel", (PyCFunction)get_debuglevel, + METH_VARARGS, + "Set the current debug level. + +Example: + +>>> spoolss.get_debuglevel() +0" }, + + { "set_debuglevel", (PyCFunction)set_debuglevel, + METH_VARARGS, + "Get the current debug level. + +Example: + +>>> spoolss.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/source4/python/py_lsa.h b/source4/python/py_lsa.h new file mode 100644 index 0000000000..99f3de50b1 --- /dev/null +++ b/source4/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/source4/python/py_ntsec.c b/source4/python/py_ntsec.c new file mode 100644 index 0000000000..47524d8e19 --- /dev/null +++ b/source4/python/py_ntsec.c @@ -0,0 +1,281 @@ +/* + 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 = PyDict_New(); + + PyDict_SetItemString(*dict, "type", PyInt_FromLong(ace->type)); + PyDict_SetItemString(*dict, "flags", PyInt_FromLong(ace->flags)); + PyDict_SetItemString(*dict, "mask", PyInt_FromLong(ace->info.mask)); + + if (py_from_SID(&obj, &ace->trustee)) + PyDict_SetItemString(*dict, "trustee", 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; + } + + *dict = PyDict_New(); + + PyDict_SetItemString(*dict, "revision", PyInt_FromLong(acl->revision)); + + 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); + } + + PyDict_SetItemString(*dict, "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(); + + PyDict_SetItemString(*dict, "revision", PyInt_FromLong(sd->revision)); + + if (py_from_SID(&obj, sd->owner_sid)) + PyDict_SetItemString(*dict, "owner_sid", obj); + + if (py_from_SID(&obj, sd->grp_sid)) + PyDict_SetItemString(*dict, "group_sid", obj); + + if (py_from_ACL(&obj, sd->dacl)) + PyDict_SetItemString(*dict, "dacl", obj); + + if (py_from_ACL(&obj, sd->sacl)) + PyDict_SetItemString(*dict, "sacl", 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, + 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/source4/python/py_samba.c b/source4/python/py_samba.c new file mode 100644 index 0000000000..c0ade12f65 --- /dev/null +++ b/source4/python/py_samba.c @@ -0,0 +1,56 @@ +/* + 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.h" +#include "python/py_common.h" + +/* + * Module initialisation + */ + +static PyObject *lsa_open_policy(PyObject *self, PyObject *args, + PyObject *kw) +{ + return NULL; +} + +static PyMethodDef samba_methods[] = { + { NULL } +}; + +static PyMethodDef cheepy_methods[] = { + { "open_policy", (PyCFunction)lsa_open_policy, METH_VARARGS|METH_KEYWORDS, + "Foo"}, + { NULL } +}; + +void initsamba(void) +{ + PyObject *module, *new_module, *dict; + + /* Initialise module */ + + module = Py_InitModule("samba", samba_methods); + dict = PyModule_GetDict(module); + + /* Do samba initialisation */ + + py_samba_init(); +} diff --git a/source4/python/py_samr.c b/source4/python/py_samr.c new file mode 100644 index 0000000000..182671d047 --- /dev/null +++ b/source4/python/py_samr.c @@ -0,0 +1,497 @@ +/* + 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 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 PyMethodDef samr_domain_methods[] = { + { "enum_domain_groups", (PyCFunction)samr_enum_dom_groups, + METH_VARARGS | METH_KEYWORDS, "Enumerate domain groups" }, + { 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 */ +}; + +/* SAMR user handle object */ + +static void py_samr_user_hnd_dealloc(PyObject* self) +{ + PyObject_Del(self); +} + +static PyMethodDef samr_user_methods[] = { + { 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 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; +} + +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[] = { + { 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/source4/python/py_samr.h b/source4/python/py_samr.h new file mode 100644 index 0000000000..3292eb97ec --- /dev/null +++ b/source4/python/py_samr.h @@ -0,0 +1,81 @@ +/* + 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); +#endif /* _PY_SAMR_H */ diff --git a/source4/python/py_samr_conv.c b/source4/python/py_samr_conv.c new file mode 100644 index 0000000000..fdf71641e0 --- /dev/null +++ b/source4/python/py_samr_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_samr.h" +#include "python/py_conv.h" + +/* + * 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/source4/python/py_smb.c b/source4/python/py_smb.c new file mode 100644 index 0000000000..3f05b5d7c6 --- /dev/null +++ b/source4/python/py_smb.c @@ -0,0 +1,399 @@ +/* + 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 = lp_netbios_name(); + + 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, desired_access, file_attributes, + share_access, create_disposition, create_options); + + 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; + SEC_DESC *secdesc = NULL; + int fnum; + TALLOC_CTX *mem_ctx; + + /* 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"); + return NULL; + } + + 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"); + result = NULL; + 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 *py_secdesc; + SEC_DESC *secdesc; + TALLOC_CTX *mem_ctx = talloc_init("py_smb_set_secdesc"); + int fnum; + BOOL result; + + /* Parse parameters */ + + if (!PyArg_ParseTupleAndKeywords( + args, kw, "iO", kwlist, &fnum, &py_secdesc)) + return NULL; + + if (!py_to_SECDESC(&secdesc, py_secdesc, mem_ctx)) { + PyErr_SetString(PyExc_TypeError, + "Invalid security descriptor"); + return NULL; + } + + result = cli_set_secdesc(cli->cli, fnum, secdesc); + + if (cli_is_error(cli->cli)) { + PyErr_SetString(PyExc_RuntimeError, "set_secdesc failed"); + return NULL; + } + + return PyInt_FromLong(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" }, + + { NULL } +}; + +static void py_cli_state_dealloc(PyObject* self) +{ + 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 = 10; +} diff --git a/source4/python/py_smb.h b/source4/python/py_smb.h new file mode 100644 index 0000000000..31bcf4aab2 --- /dev/null +++ b/source4/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/source4/python/py_spoolss.c b/source4/python/py_spoolss.c new file mode 100644 index 0000000000..7b0a102b31 --- /dev/null +++ b/source4/python/py_spoolss.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_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. + +Optionally a dictionary of (domain, username, password) may be given in +which case they are used when opening the RPC pipe. An access mask may +also be given which defaults to MAXIMUM_ALLOWED_ACCESS. + +Example: + +>>> hnd = spoolss.openprinter(\"\\\\\\\\NPSD-PDC2\\\\meanie\")"}, + + { "closeprinter", spoolss_closeprinter, METH_VARARGS, + "Close a printer handle opened with openprinter or addprinter. + +Example: + +>>> 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. + +Return a list of printers on a print server. The credentials, info level +and flags may be specified as keyword arguments. + +Example: + +>>> print spoolss.enumprinters(\"\\\\\\\\npsd-pdc2\") +[{'comment': 'i am a comment', 'printer_name': 'meanie', 'flags': 8388608, + 'description': 'meanie,Generic / Text Only,i am a location'}, + {'comment': '', 'printer_name': 'fileprint', 'flags': 8388608, + 'description': 'fileprint,Generic / Text Only,'}]"}, + + { "enumports", (PyCFunction)spoolss_enumports, + METH_VARARGS | METH_KEYWORDS, + "Enumerate ports on a print server. + +Return a list of ports on a print server. + +Example: + +>>> print spoolss.enumports(\"\\\\\\\\npsd-pdc2\") +[{'name': 'LPT1:'}, {'name': 'LPT2:'}, {'name': 'COM1:'}, {'name': 'COM2:'}, + {'name': 'FILE:'}, {'name': '\\\\nautilus1\\zpekt3r'}]"}, + + { "enumprinterdrivers", (PyCFunction)spoolss_enumprinterdrivers, + METH_VARARGS | METH_KEYWORDS, + "Enumerate printer drivers on a print server. + +Return a list of printer drivers."}, + /* Miscellaneous other commands */ + + { "getprinterdriverdir", (PyCFunction)spoolss_getprinterdriverdir, + METH_VARARGS | METH_KEYWORDS, + "Return printer driver directory. + +Return the printer driver directory for a given architecture. The +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. + +Initialises Samba's debug logging system. One argument is expected which +is a boolean specifying whether debugging is interactive and sent to stdout +or logged to a file. + +Example: + +>>> spoolss.setup_logging(interactive = 1)" }, + + { "get_debuglevel", (PyCFunction)get_debuglevel, + METH_VARARGS, + "Set the current debug level. + +Example: + +>>> spoolss.get_debuglevel() +0" }, + + { "set_debuglevel", (PyCFunction)set_debuglevel, + METH_VARARGS, + "Get the current debug level. + +Example: + +>>> 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. + +Return a dictionary of print information. The info level defaults to 1. + +Example: + +>>> hnd.getprinter() +{'comment': 'i am a comment', 'printer_name': '\\\\NPSD-PDC2\\meanie', + 'description': '\\\\NPSD-PDC2\\meanie,Generic / Text Only,i am a location', + '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. + +Return a dictionary of printer driver information for the printer driver +bound to this printer."}, + + /* Forms */ + + { "enumforms", (PyCFunction)spoolss_hnd_enumforms, + METH_VARARGS | METH_KEYWORDS, + "Enumerate supported forms. + +Return a list of forms supported by this printer or print server."}, + + { "setform", (PyCFunction)spoolss_hnd_setform, + METH_VARARGS | METH_KEYWORDS, + "Set form data. + +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/source4/python/py_spoolss.h b/source4/python/py_spoolss.h new file mode 100644 index 0000000000..34b48190cd --- /dev/null +++ b/source4/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/source4/python/py_spoolss_common.c b/source4/python/py_spoolss_common.c new file mode 100644 index 0000000000..f34d2ac6c7 --- /dev/null +++ b/source4/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/source4/python/py_spoolss_drivers.c b/source4/python/py_spoolss_drivers.c new file mode 100644 index 0000000000..a072ac0d5c --- /dev/null +++ b/source4/python/py_spoolss_drivers.c @@ -0,0 +1,421 @@ +/* + 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"; + 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, + arch, &ctr); + + if (W_ERROR_V(werror) == ERRinsufficientbuffer) + werror = cli_spoolss_getprinterdriver( + hnd->cli, hnd->mem_ctx, needed, NULL, &hnd->pol, + level, 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/source4/python/py_spoolss_drivers_conv.c b/source4/python/py_spoolss_drivers_conv.c new file mode 100644 index 0000000000..9bc8408052 --- /dev/null +++ b/source4/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/source4/python/py_spoolss_forms.c b/source4/python/py_spoolss_forms.c new file mode 100644 index 0000000000..ef9ed94533 --- /dev/null +++ b/source4/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, strlen(form_name) + 1); + 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/source4/python/py_spoolss_forms_conv.c b/source4/python/py_spoolss_forms_conv.c new file mode 100644 index 0000000000..095a318fd2 --- /dev/null +++ b/source4/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, strlen(name) + 1); + + result = True; + +done: + Py_DECREF(dict_copy); + return result; +} diff --git a/source4/python/py_spoolss_jobs.c b/source4/python/py_spoolss_jobs.c new file mode 100644 index 0000000000..59754bd36d --- /dev/null +++ b/source4/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/source4/python/py_spoolss_jobs_conv.c b/source4/python/py_spoolss_jobs_conv.c new file mode 100644 index 0000000000..cb04ec6713 --- /dev/null +++ b/source4/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/source4/python/py_spoolss_ports.c b/source4/python/py_spoolss_ports.c new file mode 100644 index 0000000000..ddc8868f0f --- /dev/null +++ b/source4/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/source4/python/py_spoolss_ports_conv.c b/source4/python/py_spoolss_ports_conv.c new file mode 100644 index 0000000000..3f6d94bf7e --- /dev/null +++ b/source4/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/source4/python/py_spoolss_printerdata.c b/source4/python/py_spoolss_printerdata.c new file mode 100644 index 0000000000..f165475b08 --- /dev/null +++ b/source4/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/source4/python/py_spoolss_printers.c b/source4/python/py_spoolss_printers.c new file mode 100644 index 0000000000..d011681acc --- /dev/null +++ b/source4/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/source4/python/py_spoolss_printers_conv.c b/source4/python/py_spoolss_printers_conv.c new file mode 100644 index 0000000000..f7b2f516df --- /dev/null +++ b/source4/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/source4/python/py_srvsvc.c b/source4/python/py_srvsvc.c new file mode 100644 index 0000000000..8ec2430285 --- /dev/null +++ b/source4/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. + +Initialises Samba's debug logging system. One argument is expected which +is a boolean specifying whether debugging is interactive and sent to stdout +or logged to a file. + +Example: + +>>> srvsvc.setup_logging(interactive = 1)" }, + + { "get_debuglevel", (PyCFunction)get_debuglevel, + METH_VARARGS, + "Set the current debug level. + +Example: + +>>> srvsvc.get_debuglevel() +0" }, + + { "set_debuglevel", (PyCFunction)set_debuglevel, + METH_VARARGS, + "Get the current debug level. + +Example: + +>>> 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/source4/python/py_srvsvc.h b/source4/python/py_srvsvc.h new file mode 100644 index 0000000000..b440c32e13 --- /dev/null +++ b/source4/python/py_srvsvc.h @@ -0,0 +1,26 @@ +/* + 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" + +#endif /* _PY_SRVSVC_H */ diff --git a/source4/python/py_srvsvc_conv.c b/source4/python/py_srvsvc_conv.c new file mode 100644 index 0000000000..de43f070ed --- /dev/null +++ b/source4/python/py_srvsvc_conv.c @@ -0,0 +1,43 @@ +/* + 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) +{ + PyObject *obj; + + *dict = from_struct(info, py_SRV_INFO_101); + + PyDict_SetItemString(*dict, "level", PyInt_FromLong(101)); + + return True; +} diff --git a/source4/python/py_tdb.c b/source4/python/py_tdb.c new file mode 100644 index 0000000000..4969c1047e --- /dev/null +++ b/source4/python/py_tdb.c @@ -0,0 +1,614 @@ +/* + Python wrappers for TDB module + + 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. +*/ + +/* + 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" +#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); +} + +/* + * 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 }, + { 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/source4/python/py_tdb.h b/source4/python/py_tdb.h new file mode 100644 index 0000000000..69f251c8c1 --- /dev/null +++ b/source4/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/source4/python/py_tdbpack.c b/source4/python/py_tdbpack.c new file mode 100644 index 0000000000..f0718b717e --- /dev/null +++ b/source4/python/py_tdbpack.c @@ -0,0 +1,725 @@ +/* -*- 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. + +This module is conceptually similar to the standard 'struct' module, but it +uses both a different binary format and a different description string. + +Samba's encoding is based on that used inside DCE-RPC and SMB: a +little-endian, unpadded, non-self-describing binary format. It is intended +that these functions be as similar as possible to the routines in Samba's +tdb/tdbutil module, with appropriate adjustments for Python datatypes. + +Python strings are used to specify the format of data to be packed or +unpacked. + +String encodings are implied by the database format: they may be either DOS +codepage (currently hardcoded to 850), or Unix codepage (currently hardcoded +to be the same as the default Python encoding). + +tdbpack format strings: + + 'f': NUL-terminated string in codepage iso8859-1 + + 'P': same as 'f' + + 'F': NUL-terminated string in iso-8859-1 + + 'd': 4 byte little-endian unsigned number + + 'w': 2 byte little-endian unsigned number + + 'P': \"Pointer\" value -- in the subset of DCERPC used by Samba, this is + really just an \"exists\" or \"does not exist\" flag. The boolean + value of the Python object is used. + + 'B': 4-byte LE length, followed by that many bytes of binary data. + Corresponds to a Python integer giving the length, followed by a byte + string of the appropriate length. + + '$': Special flag indicating that the preceding format code should be + repeated while data remains. This is only supported for unpacking. + + Every code corresponds to a single Python object, except 'B' which + corresponds to two values (length and contents), and '$', which produces + however many make sense. +"; + + +static char const pytdbpack_doc[] = +"pack(format, values) -> buffer +Pack Python objects into Samba binary format according to format string. + +arguments: + format -- string of tdbpack format characters + values -- sequence of value objects corresponding 1:1 to format characters + +returns: + buffer -- string containing packed data + +raises: + IndexError -- if there are too few values for the format + ValueError -- if any of the format characters is illegal + TypeError -- if the format is not a string, or values is not a sequence, + or any of the values is of the wrong type for the corresponding + format character + +notes: + For historical reasons, it is not an error to pass more values than are consumed + by the format. +"; + + +static char const pytdbunpack_doc[] = +"unpack(format, buffer) -> (values, rest) +Unpack Samba binary data according to format string. + +arguments: + format -- string of tdbpack characters + buffer -- string of packed binary data + +returns: + 2-tuple of: + values -- sequence of values corresponding 1:1 to format characters + rest -- string containing data that was not decoded, or '' if the + whole string was consumed + +raises: + IndexError -- if there is insufficient data in the buffer for the + format (or if the data is corrupt and contains a variable-length + field extending past the end) + ValueError -- if any of the format characters is illegal + +notes: + Because unconsumed data is returned, you can feed it back in to the + unpacker to extract further fields. Alternatively, if you wish to modify + some fields near the start of the data, you may be able to save time by + only unpacking and repacking the necessary part. +"; + + +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/source4/python/py_winbind.c b/source4/python/py_winbind.c new file mode 100644 index 0000000000..e9fc4b7dd8 --- /dev/null +++ b/source4/python/py_winbind.c @@ -0,0 +1,724 @@ +/* + Unix SMB/CIFS implementation. + + Python wrapper for winbind client functions. + + 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_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())); + + /* Winbind uid/gid range */ + + if (lp_winbind_uid(&ulow, &uhi)) { + PyDict_SetItemString(result, "uid_low", PyInt_FromLong(ulow)); + PyDict_SetItemString(result, "uid_high", PyInt_FromLong(uhi)); + } + + if (lp_winbind_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); + + fstrcpy(request.data.auth_crap.user, username); + + 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); +} + +/* 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 + +Return the SID for a name. + +Example: + +>>> winbind.name_to_sid('FOO/Administrator') +'S-1-5-21-406022937-1377575209-526660263-500' " }, + + { "sid_to_name", (PyCFunction)py_sid_to_name, METH_VARARGS, + "sid_to_name(s) -> string + +Return the name for a SID. + +Example: + +>>> import winbind +>>> winbind.sid_to_name('S-1-5-21-406022937-1377575209-526660263-500') +'FOO/Administrator' " }, + + /* Enumerate users/groups */ + + { "enum_domain_users", (PyCFunction)py_enum_domain_users, METH_VARARGS, + "enum_domain_users() -> list of strings + +Return a list of domain users. + +Example: + +>>> winbind.enum_domain_users() +['FOO/Administrator', 'FOO/anna', 'FOO/Anne Elk', 'FOO/build', +'FOO/foo', 'FOO/foo2', 'FOO/foo3', 'FOO/Guest', 'FOO/user1', +'FOO/whoops-ptang'] " }, + + { "enum_domain_groups", (PyCFunction)py_enum_domain_groups, + METH_VARARGS, + "enum_domain_groups() -> list of strings + +Return a list of domain groups. + +Example: + +>>> winbind.enum_domain_groups() +['FOO/cows', 'FOO/Domain Admins', 'FOO/Domain Guests', +'FOO/Domain Users'] " }, + + /* ID mapping */ + + { "uid_to_sid", (PyCFunction)py_uid_to_sid, METH_VARARGS, + "uid_to_sid(int) -> string + +Return the SID for a UNIX uid. + +Example: + +>>> winbind.uid_to_sid(10000) +'S-1-5-21-406022937-1377575209-526660263-500' " }, + + { "gid_to_sid", (PyCFunction)py_gid_to_sid, METH_VARARGS, + "gid_to_sid(int) -> string + +Return the UNIX gid for a SID. + +Example: + +>>> winbind.gid_to_sid(10001) +'S-1-5-21-406022937-1377575209-526660263-512' " }, + + { "sid_to_uid", (PyCFunction)py_sid_to_uid, METH_VARARGS, + "sid_to_uid(string) -> int + +Return the UNIX uid for a SID. + +Example: + +>>> winbind.sid_to_uid('S-1-5-21-406022937-1377575209-526660263-500') +10000 " }, + + { "sid_to_gid", (PyCFunction)py_sid_to_gid, METH_VARARGS, + "sid_to_gid(string) -> int + +Return the UNIX gid corresponding to a SID. + +Example: + +>>> winbind.sid_to_gid('S-1-5-21-406022937-1377575209-526660263-512') +10001 " }, + + /* Miscellaneous */ + + { "check_secret", (PyCFunction)py_check_secret, METH_VARARGS, + "check_secret() -> int + +Check the machine trust account password. The NT status is returned +with zero indicating success. " }, + + { "enum_trust_dom", (PyCFunction)py_enum_trust_dom, METH_VARARGS, + "enum_trust_dom() -> list of strings + +Return a list of trusted domains. The domain the server is a member +of is not included. + +Example: + +>>> winbind.enum_trust_dom() +['NPSD-TEST2', 'SP2NDOM'] " }, + + /* PAM authorisation functions */ + + { "auth_plaintext", (PyCFunction)py_auth_plaintext, METH_VARARGS, + "auth_plaintext(s, s) -> int + +Authenticate a username and password using plaintext authentication. +The NT status code is returned with zero indicating success." }, + + { "auth_crap", (PyCFunction)py_auth_crap, METH_VARARGS, + "auth_crap(s, s) -> int + +Authenticate a username and password using the challenge/response +protocol. The NT status code is returned with zero indicating +success." }, + + { 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/source4/python/py_winbind.h b/source4/python/py_winbind.h new file mode 100644 index 0000000000..10927ea6c8 --- /dev/null +++ b/source4/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/source4/python/py_winbind_conv.c b/source4/python/py_winbind_conv.c new file mode 100644 index 0000000000..6e2eab5941 --- /dev/null +++ b/source4/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/source4/python/py_winreg.c b/source4/python/py_winreg.c new file mode 100644 index 0000000000..ce27f5c533 --- /dev/null +++ b/source4/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/source4/python/py_winreg.h b/source4/python/py_winreg.h new file mode 100644 index 0000000000..e19674d218 --- /dev/null +++ b/source4/python/py_winreg.h @@ -0,0 +1,29 @@ +/* + 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 "includes.h" +#include "Python.h" + +#include "python/py_common.h" + +#endif /* _PY_WINREG_H */ diff --git a/source4/python/samba/.cvsignore b/source4/python/samba/.cvsignore new file mode 100644 index 0000000000..0d20b6487c --- /dev/null +++ b/source4/python/samba/.cvsignore @@ -0,0 +1 @@ +*.pyc diff --git a/source4/python/samba/__init__.py b/source4/python/samba/__init__.py new file mode 100644 index 0000000000..c818ca3e04 --- /dev/null +++ b/source4/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/source4/python/samba/printerdata.py b/source4/python/samba/printerdata.py new file mode 100644 index 0000000000..33251f6a00 --- /dev/null +++ b/source4/python/samba/printerdata.py @@ -0,0 +1,59 @@ +#!/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 = {}): + self.hnd = spoolss.openprinter(host, creds = creds) + + 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): + self.host = host + self.top_level_keys = ["PrinterDriverData", "DsSpooler", "DsDriver", + "DsUser"] + + 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): + self.hnd = spoolss.openprinter(host) + 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) diff --git a/source4/python/setup.py b/source4/python/setup.py new file mode 100755 index 0000000000..48487fee4d --- /dev/null +++ b/source4/python/setup.py @@ -0,0 +1,198 @@ +# -*- 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", "") + +# Convert libs and objs from space separated strings to lists of strings +# for distutils to digest. Split "-l" prefix off library list. + +obj_list = string.split(samba_objs) + +lib_list = [] + +for lib in string.split(samba_libs): + lib_list.append(string.replace(lib, "-l", "")) + +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 = lib_list, + library_dirs = ["/usr/kerberos/lib"], + 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 = lib_list, + library_dirs = ["/usr/kerberos/lib"], + 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_samr_conv.c", + samba_srcdir + "python/py_common.c"], + libraries = lib_list, + library_dirs = ["/usr/kerberos/lib"], + 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 = lib_list, + library_dirs = ["/usr/kerberos/lib"], + 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 = lib_list, + library_dirs = ["/usr/kerberos/lib"], + 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 = lib_list, + library_dirs = ["/usr/kerberos/lib"], + extra_compile_args = flags_list, + extra_objects = obj_list), + + # tdb module + + Extension(name = "tdb", + sources = [samba_srcdir + "python/py_tdb.c"], + libraries = lib_list, + library_dirs = ["/usr/kerberos/lib"], + 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 = lib_list, + library_dirs = ["/usr/kerberos/lib"], + extra_compile_args = flags_list, + extra_objects = obj_list), + + # Moving to merge all individual extensions in to one big + # extension. This is to avoid the fact that each extension is 3MB + # in size due to the lack of proper depedency management in Samba. + + Extension(name = "samba", + sources = [samba_srcdir + "python/py_samba.c", + samba_srcdir + "python/py_common.c"], + libraries = lib_list, + library_dirs = ["/usr/kerberos/lib"], + 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/source4/registry/reg_cachehook.c b/source4/registry/reg_cachehook.c new file mode 100644 index 0000000000..547eed392d --- /dev/null +++ b/source4/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, ®db_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/source4/registry/reg_db.c b/source4/registry/reg_db.c new file mode 100644 index 0000000000..b0917c8f60 --- /dev/null +++ b/source4/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 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_by_string( 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= 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/source4/registry/reg_objects.c b/source4/registry/reg_objects.c new file mode 100644 index 0000000000..9cfeb7faa9 --- /dev/null +++ b/source4/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; inum_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; inum_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/source4/registry/reg_printing.c b/source4/registry/reg_printing.c new file mode 100644 index 0000000000..619ffc7ee7 --- /dev/null +++ b/source4/registry/reg_printing.c @@ -0,0 +1,829 @@ +/* + * 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 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\\ + *********************************************************************/ + +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, strlen(filename)+1 ); + 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, strlen(filename)+1 ); + 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, strlen(filename)+1 ); + 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, strlen(filename)+1 ); + regval_ctr_addvalue( val, "Help File", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) ); + + init_unistr2( &data, info3->defaultdatatype, strlen(info3->defaultdatatype)+1 ); + 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, length+1 ); + 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; iwidth; + 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; iwidth; + 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; snuminfo_2->data, new_path?new_path:"", &subkey_names ); + + for ( i=0; iinfo_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, strlen(info2->printername)+1 ); + regval_ctr_addvalue( val, "Name", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) ); + init_unistr2( &data, info2->location, strlen(info2->location)+1 ); + regval_ctr_addvalue( val, "Location", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) ); + init_unistr2( &data, info2->comment, strlen(info2->comment)+1 ); + regval_ctr_addvalue( val, "Comment", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) ); + init_unistr2( &data, info2->parameters, strlen(info2->parameters)+1 ); + regval_ctr_addvalue( val, "Parameters", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) ); + init_unistr2( &data, info2->portname, strlen(info2->portname)+1 ); + regval_ctr_addvalue( val, "Port", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) ); + init_unistr2( &data, info2->servername, strlen(info2->servername)+1 ); + regval_ctr_addvalue( val, "Server", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) ); + init_unistr2( &data, info2->sharename, strlen(info2->sharename)+1 ); + regval_ctr_addvalue( val, "Share", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) ); + init_unistr2( &data, info2->drivername, strlen(info2->drivername)+1 ); + regval_ctr_addvalue( val, "Driver", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) ); + init_unistr2( &data, info2->sepfile, strlen(info2->sepfile)+1 ); + regval_ctr_addvalue( val, "Separator File", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) ); + init_unistr2( &data, "winprint", strlen("winprint")+1 ); + 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; ikeys[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[%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[%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/source4/rpc_client/cli_dfs.c b/source4/rpc_client/cli_dfs.c new file mode 100644 index 0000000000..2136b69df0 --- /dev/null +++ b/source4/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/source4/rpc_client/cli_ds.c b/source4/rpc_client/cli_ds.c new file mode 100644 index 0000000000..f0edeca000 --- /dev/null +++ b/source4/rpc_client/cli_ds.c @@ -0,0 +1,73 @@ +/* + 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 */ + +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("", &q, &qbuf, 0) + || !rpc_api_pipe_req(cli, DS_GETPRIMDOMINFO, &qbuf, &rbuf)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* Unmarshall response */ + + if (!ds_io_r_getprimdominfo("", &r, &rbuf, 0)) { + 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 (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; +} diff --git a/source4/rpc_client/cli_lsarpc.c b/source4/rpc_client/cli_lsarpc.c new file mode 100644 index 0000000000..bbd40b2ef7 --- /dev/null +++ b/source4/rpc_client/cli_lsarpc.c @@ -0,0 +1,1441 @@ +/* + 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; + } + + 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; + } + + 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)) { + *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, 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; + (*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, + fstring 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 */ + + ZERO_STRUCTP(domain_sid); + domain_name[0] = '\0'; + + switch (info_class) { + + case 3: + if (r.dom.id3.buffer_dom_name != 0) { + unistr2_to_ascii(domain_name, + &r.dom.id3. + uni_domain_name, + sizeof (fstring) - 1); + } + + if (r.dom.id3.buffer_dom_sid != 0) { + *domain_sid = r.dom.id3.dom_sid.sid; + } + + break; + + case 5: + + if (r.dom.id5.buffer_dom_name != 0) { + unistr2_to_ascii(domain_name, &r.dom.id5. + uni_domain_name, + sizeof (fstring) - 1); + } + + if (r.dom.id5.buffer_dom_sid != 0) { + *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, + fstring domain_name, fstring dns_name, + fstring forest_name, GUID *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_sid); + ZERO_STRUCTP(domain_guid); + domain_name[0] = '\0'; + + if (r.info.dns_dom_info.hdr_nb_dom_name.buffer) { + unistr2_to_ascii(domain_name, + &r.info.dns_dom_info.uni_nb_dom_name, + sizeof(fstring) - 1); + } + if (r.info.dns_dom_info.hdr_dns_dom_name.buffer) { + unistr2_to_ascii(dns_name, + &r.info.dns_dom_info.uni_dns_dom_name, + sizeof(fstring) - 1); + } + if (r.info.dns_dom_info.hdr_forest_name.buffer) { + unistr2_to_ascii(forest_name, + &r.info.dns_dom_info.uni_forest_name, + sizeof(fstring) - 1); + } + + memcpy(domain_guid, &r.info.dns_dom_info.dom_guid, sizeof(GUID)); + + if (r.info.dns_dom_info.ptr_dom_sid != 0) { + *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; +} + +/** 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; imem_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", + lp_netbios_name(), cli->desthost, credstr(clnt_chal->data))); + + /* store the parameters */ + init_q_req_chal(&q, cli->srv_name_slash, lp_netbios_name(), 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, lp_netbios_name(), + 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, lp_netbios_name(), &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; + } + } + + 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, lp_netbios_name(), + 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, lp_netbios_name(), &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; + *neg_flags = r.srv_flgs.neg_flags; + + 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; + } + } + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/* Return the secure channel type depending on the server role. */ + +uint16 get_sec_chan(void) +{ + uint16 sec_chan = SEC_CHAN_WKSTA; + + switch (lp_server_role()) { + case ROLE_DOMAIN_PDC: + sec_chan = SEC_CHAN_DOMAIN; + break; + case ROLE_DOMAIN_BDC: + sec_chan = SEC_CHAN_BDC; + break; + } + + return sec_chan; +} + +/* 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(1,("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; +} + +/**************************************************************************** +Generate the next creds to use. Yuck - this is a cut&paste from another +file. They should be combined at some stage. )-: +****************************************************************************/ + +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, + 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); + + /* 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; + + memset(&dummy_rtn_creds, '\0', sizeof(dummy_rtn_creds)); + dummy_rtn_creds.timestamp.time = time(NULL); + + 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, + 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, lp_netbios_name(), + &clnt_creds, &dummy_rtn_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; + + 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, + 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; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + 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; + + memset(&dummy_rtn_creds, '\0', sizeof(dummy_rtn_creds)); + dummy_rtn_creds.timestamp.time = time(NULL); + + 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, lp_netbios_name(), + &clnt_creds, &dummy_rtn_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; + } + + /* Return results */ + + result = r.status; + + 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; + char *mach_acct; + + gen_next_creds( cli, &new_clnt_cred); + + prs_init(&qbuf , 1024, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* create and send a MSRPC command with api NET_SRV_PWSET */ + + mach_acct = talloc_asprintf(mem_ctx, "%s$", machine_name); + + if (!mach_acct) { + DEBUG(0,("talloc_asprintf failed!\n")); + nt_status = NT_STATUS_NO_MEMORY; + goto done; + } + + DEBUG(4,("cli_net_srv_pwset: srv:%s acct:%s sc: %d mc: %s clnt %s %x\n", + cli->srv_name_slash, 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, cli->sess_key, + mach_acct, sec_chan_type, machine_name, + &new_clnt_cred, (char *)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/source4/rpc_client/cli_pipe.c b/source4/rpc_client/cli_pipe.c new file mode 100644 index 0000000000..0fa2b4ad40 --- /dev/null +++ b/source4/rpc_client/cli_pipe.c @@ -0,0 +1,1357 @@ +/* + * 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. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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[]; + +/******************************************************************** + 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); +} + +static void NTLMSSPcalc_ap( struct cli_state *cli, unsigned char *data, uint32 len) +{ + unsigned char *hash = cli->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; +} + +/**************************************************************************** + 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, int len, int auth_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) + RPC_HEADER_LEN + RPC_HDR_REQ_LEN; + + BOOL auth_verify = ((cli->ntlmssp_srv_flgs & NTLMSSP_NEGOTIATE_SIGN) != 0); + BOOL auth_seal = ((cli->ntlmssp_srv_flgs & NTLMSSP_NEGOTIATE_SEAL) != 0); + + DEBUG(5,("rpc_auth_pipe: len: %d auth_len: %d verify %s seal %s\n", + len, auth_len, BOOLSTR(auth_verify), BOOLSTR(auth_seal))); + + /* + * Unseal any sealed data in the PDU, not including the + * 8 byte auth_header or the auth_data. + */ + + if (auth_seal) { + DEBUG(10,("rpc_auth_pipe: unseal\n")); + dump_data(100, reply_data, data_len); + NTLMSSPcalc_ap(cli, (uchar*)reply_data, data_len); + dump_data(100, reply_data, data_len); + } + + if (auth_verify || auth_seal) { + RPC_HDR_AUTH rhdr_auth; + prs_struct auth_req; + char data[RPC_HDR_AUTH_LEN]; + /* + * We set dp to be the end of the packet, minus the auth_len + * and the length of the header that preceeds the auth_data. + */ + char *dp = prs_data_p(rdata) + len - auth_len - RPC_HDR_AUTH_LEN; + + if(dp - prs_data_p(rdata) > prs_data_size(rdata)) { + DEBUG(0,("rpc_auth_pipe: auth data > data size !\n")); + return False; + } + + memcpy(data, dp, sizeof(data)); + + prs_init(&auth_req , 0, cli->mem_ctx, UNMARSHALL); + + /* The endianness must be preserved... JRA. */ + + prs_set_endian_data(&auth_req, rdata->bigendian_data); + + prs_give_memory(&auth_req, data, RPC_HDR_AUTH_LEN, False); + + /* + * Unmarshall the 8 byte auth_header that comes before the + * auth data. + */ + + if(!smb_io_rpc_hdr_auth("hdr_auth", &rhdr_auth, &auth_req, 0)) { + DEBUG(0,("rpc_auth_pipe: unmarshalling RPC_HDR_AUTH failed.\n")); + return False; + } + + if (!rpc_hdr_auth_chk(&rhdr_auth)) { + DEBUG(0,("rpc_auth_pipe: rpc_hdr_auth_chk failed.\n")); + return False; + } + } + + /* + * Now unseal and check the auth verifier in the auth_data at + * then end of the packet. The 4 bytes skipped in the unseal + * seem to be a buffer pointer preceeding the sealed data. + */ + + if (auth_verify) { + RPC_AUTH_NTLMSSP_CHK chk; + uint32 crc32; + prs_struct auth_verf; + char data[RPC_AUTH_NTLMSSP_CHK_LEN]; + char *dp = prs_data_p(rdata) + len - auth_len; + + if(dp - prs_data_p(rdata) > prs_data_size(rdata)) { + DEBUG(0,("rpc_auth_pipe: auth data > data size !\n")); + return False; + } + + DEBUG(10,("rpc_auth_pipe: verify\n")); + dump_data(100, dp, auth_len); + NTLMSSPcalc_ap(cli, (uchar*)(dp+4), auth_len - 4); + + memcpy(data, dp, RPC_AUTH_NTLMSSP_CHK_LEN); + dump_data(100, data, 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); + + prs_give_memory(&auth_verf, data, RPC_AUTH_NTLMSSP_CHK_LEN, False); + + if(!smb_io_rpc_auth_ntlmssp_chk("auth_sign", &chk, &auth_verf, 0)) { + DEBUG(0,("rpc_auth_pipe: unmarshalling RPC_AUTH_NTLMSSP_CHK failed.\n")); + return False; + } + + crc32 = crc32_calc_buffer(reply_data, data_len); + + if (!rpc_auth_ntlmssp_chk(&chk, crc32 , cli->ntlmssp_seq_num)) { + DEBUG(0,("rpc_auth_pipe: rpc_auth_ntlmssp_chk failed.\n")); + return False; + } + cli->ntlmssp_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 neogitated. + + ****************************************************************************/ + +static BOOL rpc_api_pipe(struct cli_state *cli, prs_struct *data, prs_struct *rdata) +{ + 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 max_data = cli->max_xmit_frag ? cli->max_xmit_frag : 1024; + + /* 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_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; + } + } + + 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, ¤t_offset)) { + prs_mem_free(rdata); + return False; + } + } + + /* + * Now we have a complete PDU, check the auth struct if any was sent. + */ + + if (rhdr.auth_len != 0) { + if(!rpc_auth_pipe(cli, rdata, rhdr.frag_len, rhdr.auth_len)) + return False; + /* + * 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 -= (rhdr.auth_len + RPC_HDR_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, ¤t_offset)) + return False; + + /* + * Verify any authentication footer. + */ + + if (rhdr.auth_len != 0 ) { + if(!rpc_auth_pipe(cli, rdata, rhdr.frag_len, rhdr.auth_len)) + return False; + /* + * 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 -= (rhdr.auth_len + RPC_HDR_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 BOOL create_rpc_bind_req(prs_struct *rpc_out, BOOL do_auth, uint32 rpc_call_id, + RPC_IFACE *abstract, RPC_IFACE *transfer, + const char *my_name, const char *domain, uint32 neg_flags) +{ + RPC_HDR hdr; + RPC_HDR_RB hdr_rb; + char buffer[4096]; + prs_struct auth_info; + int auth_len = 0; + + prs_init(&auth_info, 0, prs_get_mem_context(rpc_out), MARSHALL); + + if (do_auth) { + RPC_HDR_AUTH hdr_auth; + RPC_AUTH_VERIFIER auth_verifier; + RPC_AUTH_NTLMSSP_NEG ntlmssp_neg; + + /* + * Create the auth structs we will marshall. + */ + + init_rpc_hdr_auth(&hdr_auth, NTLMSSP_AUTH_TYPE, NTLMSSP_AUTH_LEVEL, 0x00, 1); + init_rpc_auth_verifier(&auth_verifier, "NTLMSSP", NTLMSSP_NEGOTIATE); + init_rpc_auth_ntlmssp_neg(&ntlmssp_neg, neg_flags, my_name, domain); + + /* + * Use the 4k buffer to store the auth info. + */ + + prs_give_memory( &auth_info, buffer, sizeof(buffer), False); + + /* + * 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")); + return False; + } + + if(!smb_io_rpc_auth_verifier("auth_verifier", &auth_verifier, &auth_info, 0)) { + DEBUG(0,("create_rpc_bind_req: failed to marshall RPC_AUTH_VERIFIER.\n")); + return False; + } + + if(!smb_io_rpc_auth_ntlmssp_neg("ntlmssp_neg", &ntlmssp_neg, &auth_info, 0)) { + DEBUG(0,("create_rpc_bind_req: failed to marshall RPC_AUTH_NTLMSSP_NEG.\n")); + return False; + } + + /* Auth len in the rpc header doesn't include auth_header. */ + auth_len = prs_offset(&auth_info) - RPC_HDR_AUTH_LEN; + } + + /* 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")); + return False; + } + + /* 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")); + return False; + } + + /* + * Grow the outgoing buffer to store any auth info. + */ + + if(hdr.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")); + return False; + } + } + + return True; +} + +/******************************************************************* + 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 BOOL create_rpc_bind_resp(struct pwd_info *pwd, + const char *domain, const char *user_name, const char *my_name, + uint32 ntlmssp_cli_flgs, + uint32 rpc_call_id, + prs_struct *rpc_out) +{ + unsigned char lm_owf[24]; + unsigned char nt_owf[24]; + RPC_HDR hdr; + RPC_HDR_AUTHA hdr_autha; + RPC_AUTH_VERIFIER auth_verifier; + RPC_AUTH_NTLMSSP_RESP ntlmssp_resp; + char buffer[4096]; + prs_struct auth_info; + + /* + * Marshall the variable length data into a temporary parse + * struct, pointing into a 4k local buffer. + */ + prs_init(&auth_info, 0, prs_get_mem_context(rpc_out), MARSHALL); + + /* + * Use the 4k buffer to store the auth info. + */ + + prs_give_memory( &auth_info, buffer, sizeof(buffer), False); + + /* + * Create the variable length auth_data. + */ + + init_rpc_auth_verifier(&auth_verifier, "NTLMSSP", NTLMSSP_AUTH); + + pwd_get_lm_nt_owf(pwd, lm_owf, nt_owf); + + init_rpc_auth_ntlmssp_resp(&ntlmssp_resp, + lm_owf, nt_owf, + domain, user_name, my_name, + ntlmssp_cli_flgs); + + /* + * Marshall the variable length auth_data into a temp parse_struct. + */ + + if(!smb_io_rpc_auth_verifier("auth_verifier", &auth_verifier, &auth_info, 0)) { + DEBUG(0,("create_rpc_bind_resp: failed to marshall RPC_AUTH_VERIFIER.\n")); + return False; + } + + if(!smb_io_rpc_auth_ntlmssp_resp("ntlmssp_resp", &ntlmssp_resp, &auth_info, 0)) { + DEBUG(0,("create_rpc_bind_resp: failed to marshall RPC_AUTH_NTLMSSP_RESP.\n")); + return False; + } + + /* Create the request RPC_HDR */ + init_rpc_hdr(&hdr, RPC_BINDRESP, 0x0, rpc_call_id, + RPC_HEADER_LEN + RPC_HDR_AUTHA_LEN + prs_offset(&auth_info), + prs_offset(&auth_info) ); + + /* Marshall it. */ + if(!smb_io_rpc_hdr("hdr", &hdr, rpc_out, 0)) { + DEBUG(0,("create_rpc_bind_resp: failed to marshall RPC_HDR.\n")); + return False; + } + + /* Create the request RPC_HDR_AUTHA */ + init_rpc_hdr_autha(&hdr_autha, MAX_PDU_FRAG_LEN, MAX_PDU_FRAG_LEN, + NTLMSSP_AUTH_TYPE, NTLMSSP_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")); + return False; + } + + /* + * Append the auth data to the outgoing buffer. + */ + + if(!prs_append_prs_data(rpc_out, &auth_info)) { + DEBUG(0,("create_rpc_bind_req: failed to grow parse struct to add auth.\n")); + return False; + } + + return True; +} + + +/******************************************************************* + 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_left - RPC_HEADER_LEN - RPC_HDR_AUTH_LEN - auth_len; + else + alloc_hint = data_left - 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, BOOL auth_verify) +{ + RPC_HDR_AUTH hdr_auth; + + init_rpc_hdr_auth(&hdr_auth, NTLMSSP_AUTH_TYPE, + NTLMSSP_AUTH_LEVEL, 0x08, + (auth_verify ? 1 : 0)); + 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; +} + +/******************************************************************* + Puts auth data into an rpc request. + ********************************************************************/ + +static BOOL create_auth_data(struct cli_state *cli, uint32 crc32, + prs_struct *outgoing_packet) +{ + char *pdata_out = prs_data_p(outgoing_packet); + RPC_AUTH_NTLMSSP_CHK chk; + uint32 current_offset = prs_offset(outgoing_packet); + + init_rpc_auth_ntlmssp_chk(&chk, NTLMSSP_SIGN_VERSION, + crc32, cli->ntlmssp_seq_num++); + if(!smb_io_rpc_auth_ntlmssp_chk("auth_sign", &chk, + outgoing_packet, 0)) { + DEBUG(0,("create_auth_data: Failed to marshal RPC_AUTH_NTLMSSP_CHK.\n")); + return False; + } + NTLMSSPcalc_ap(cli, (unsigned char*) + &pdata_out[current_offset+4], + RPC_AUTH_NTLMSSP_CHK_LEN - 4); + 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, max_data, data_left, data_sent; + BOOL ret = False; + BOOL auth_verify, auth_seal; + fstring dump_name; + + auth_verify = ((cli->ntlmssp_srv_flgs & NTLMSSP_NEGOTIATE_SIGN) != 0); + auth_seal = ((cli->ntlmssp_srv_flgs & NTLMSSP_NEGOTIATE_SEAL) != 0); + + auth_len = (auth_verify ? RPC_AUTH_NTLMSSP_CHK_LEN : 0); + + /* + * 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_verify ? RPC_HDR_AUTH_LEN : 0) - auth_len; + + for (data_left = prs_offset(data), data_sent = 0; data_left > 0;) { + prs_struct outgoing_packet; + uint32 data_len, send_size; + uint8 flags = 0; + uint32 crc32 = 0; + uint32 callid = 0; + + /* + * how much will we send this time + */ + send_size = MIN(data_left, max_data); + data_len = RPC_HEADER_LEN + RPC_HDR_REQ_LEN + send_size + + (auth_verify ? RPC_HDR_AUTH_LEN : 0) + auth_len; + + /* + * 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, auth_len, flags, + callid, data_left))) { + DEBUG(0,("rpc_api_pipe_req: Failed to create RPC request.\n")); + prs_mem_free(&outgoing_packet); + return False; + } + + /* + * Seal the outgoing data if requested. + */ + if (auth_seal) { + crc32 = crc32_calc_buffer(prs_data_p(data) + data_sent, + send_size); + NTLMSSPcalc_ap(cli, (unsigned char*)prs_data_p(data) + + data_sent, send_size); + } + + /* + * Now copy the data into the outgoing packet. + */ + if(!prs_append_some_prs_data(&outgoing_packet, data, + data_sent, send_size)) { + DEBUG(0,("rpc_api_pipe_req: Failed to append data to outgoing packet.\n")); + prs_mem_free(&outgoing_packet); + return False; + } + + /* + * Add a trailing auth_verifier if needed. + */ + if (auth_seal || auth_verify) { + if(!create_auth_hdr(&outgoing_packet, auth_verify)) { + prs_mem_free(&outgoing_packet); + return False; + } + } + + /* + * Finally the auth data itself. + */ + if (auth_verify) { + if (!create_auth_data(cli, crc32, &outgoing_packet)) { + prs_mem_free(&outgoing_packet); + return False; + } + } + + 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); + 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) +{ + int i = 0; + + if ( hdr_ba->addr.len <= 0) + return False; + + if ( !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; + } + + /* 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) +{ + RPC_HDR_AUTH rhdr_auth; + RPC_AUTH_VERIFIER rhdr_verf; + RPC_AUTH_NTLMSSP_CHAL rhdr_chal; + char buffer[MAX_PDU_FRAG_LEN]; + prs_struct rpc_out; + ssize_t ret; + + unsigned char p24[24]; + unsigned char lm_owf[24]; + unsigned char lm_hash[16]; + + if(!smb_io_rpc_hdr_auth("", &rhdr_auth, rdata, 0)) { + DEBUG(0,("rpc_send_auth_reply: Failed to unmarshall RPC_HDR_AUTH.\n")); + return False; + } + if(!smb_io_rpc_auth_verifier("", &rhdr_verf, rdata, 0)) { + DEBUG(0,("rpc_send_auth_reply: Failed to unmarshall RPC_AUTH_VERIFIER.\n")); + return False; + } + if(!smb_io_rpc_auth_ntlmssp_chal("", &rhdr_chal, rdata, 0)) { + DEBUG(0,("rpc_send_auth_reply: Failed to unmarshall RPC_AUTH_NTLMSSP_CHAL.\n")); + return False; + } + + cli->ntlmssp_cli_flgs = rhdr_chal.neg_flags; + + pwd_make_lm_nt_owf(&cli->pwd, rhdr_chal.challenge); + + prs_init(&rpc_out, 0, cli->mem_ctx, MARSHALL); + + prs_give_memory( &rpc_out, buffer, sizeof(buffer), False); + + create_rpc_bind_resp(&cli->pwd, cli->domain, + cli->user_name, lp_netbios_name(), + cli->ntlmssp_cli_flgs, rpc_call_id, + &rpc_out); + + pwd_get_lm_nt_owf(&cli->pwd, lm_owf, NULL); + pwd_get_lm_nt_16(&cli->pwd, lm_hash, NULL); + + NTLMSSPOWFencrypt(lm_hash, 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++) + cli->ntlmssp_hash[ind] = (unsigned char)ind; + + for( ind = 0; ind < 256; ind++) { + unsigned char tc; + + j += (cli->ntlmssp_hash[ind] + k2[ind%8]); + + tc = cli->ntlmssp_hash[ind]; + cli->ntlmssp_hash[ind] = cli->ntlmssp_hash[j]; + cli->ntlmssp_hash[j] = tc; + } + + cli->ntlmssp_hash[256] = 0; + cli->ntlmssp_hash[257] = 0; + } + + memset((char *)lm_hash, '\0', sizeof(lm_hash)); + + 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)); + return False; + } + + cli->ntlmssp_srv_flgs = rhdr_chal.neg_flags; + return True; +} + +/**************************************************************************** + Do an rpc bind. +****************************************************************************/ + +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; + BOOL do_auth = (cli->ntlmssp_cli_flgs != 0); + 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(); + + /* Marshall the outgoing data. */ + create_rpc_bind_req(&rpc_out, do_auth, rpc_call_id, + &abstract, &transfer, + lp_netbios_name(), cli->domain, cli->ntlmssp_cli_flgs); + + /* 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_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 (do_auth && !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; +} + +/**************************************************************************** + 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(0,("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); + return False; + } + } + + /******************* bind request on pipe *****************/ + + if (!rpc_pipe_bind(cli, pipe_idx, lp_netbios_name())) { + 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); + return False; + } + + /* + * 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(cli->srv_name_slash); + + fstrcpy(cli->clnt_name_slash, "\\\\"); + fstrcat(cli->clnt_name_slash, lp_netbios_name()); + strupper(cli->clnt_name_slash); + + fstrcpy(cli->mach_acct, lp_netbios_name()); + fstrcat(cli->mach_acct, "$"); + strupper(cli->mach_acct); + + /* Remember which pipe we're talking to */ + fstrcpy(cli->pipe_name, pipe_names[pipe_idx].client_pipe); + + return True; +} + + +const char *cli_pipe_get_name(struct cli_state *cli) +{ + return cli->pipe_name; +} + + +/**************************************************************************** +close the session +****************************************************************************/ + +void cli_nt_session_close(struct cli_state *cli) +{ + cli_close(cli, cli->nt_pipe_fnum); + cli->nt_pipe_fnum = 0; +} diff --git a/source4/rpc_client/cli_reg.c b/source4/rpc_client/cli_reg.c new file mode 100644 index 0000000000..5cfbf68fb3 --- /dev/null +++ b/source4/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/source4/rpc_client/cli_samr.c b/source4/rpc_client/cli_samr.c new file mode 100644 index 0000000000..edfdb386ff --- /dev/null +++ b/source4/rpc_client/cli_samr.c @@ -0,0 +1,1445 @@ +/* + 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; + + 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; + } + + 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; + + 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; + } + + 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; + + 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)) { + *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; + + 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; + } + + 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; + + 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; + } + + 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; + + 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; + } + + 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; + + 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; +} + +/* 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; + + 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; + + 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; +} + +/* 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; + + 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; + + 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; + + 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); + + /* 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; + + 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 (!((*dom_groups) = (struct acct_info *) + talloc(mem_ctx, sizeof(struct acct_info) * *num_dom_groups))) { + result = NT_STATUS_UNSUCCESSFUL; + 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_groups, + uint32 *num_dom_groups) +{ + 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; + + 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_groups = r.num_entries2; + + if (!((*dom_groups) = (struct acct_info *) + talloc(mem_ctx, sizeof(struct acct_info) * *num_dom_groups))) { + result = NT_STATUS_UNSUCCESSFUL; + 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; +} + +/* 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; + + 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 (!(*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; + + 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; + } + + 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; + + 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; +} + +/* 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; + + 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_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; + + 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; + + 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; + + 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, + uchar sess_key[16], SAM_USERINFO_CTR *ctr) +{ + prs_struct qbuf, rbuf; + SAMR_Q_SET_USERINFO q; + SAMR_R_SET_USERINFO 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 */ + + 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, + uchar sess_key[16], SAM_USERINFO_CTR *ctr) +{ + prs_struct qbuf, rbuf; + SAMR_Q_SET_USERINFO2 q; + SAMR_R_SET_USERINFO2 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_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; + + 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; + + 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; + + 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; +} diff --git a/source4/rpc_client/cli_spoolss.c b/source4/rpc_client/cli_spoolss.c new file mode 100644 index 0000000000..bb6ce1b998 --- /dev/null +++ b/source4/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; iprs,0); + + for (i=0; iprs,0); + + for (i=0; iprs,0); + + for (i=0; iprs, 0); + + for (i=0; iprs, 0); + + for (i=0; iprs,0); + + for (i=0; iprs,0); + + for (i=0; iprs,0); + + for (i=0; iprs, 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 (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, 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 (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, 2, 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 (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 (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 (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 (client); + slprintf (server, sizeof(fstring)-1, "\\\\%s", cli->desthost); + strupper (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 (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, 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, 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, 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/source4/rpc_client/cli_spoolss_notify.c b/source4/rpc_client/cli_spoolss_notify.c new file mode 100644 index 0000000000..f4eda332bb --- /dev/null +++ b/source4/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, + ¬ify_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/source4/rpc_client/cli_srvsvc.c b/source4/rpc_client/cli_srvsvc.c new file mode 100644 index 0000000000..6cd18f2e43 --- /dev/null +++ b/source4/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 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, strlen(s) + 1); + + 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, strlen(s) + 1); + + } + + 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, strlen(s) + 1); + + 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, strlen(s) + 1); + + 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, strlen(s) + 1); + + 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, strlen(s) + 1); + } + 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, strlen(s) + 1); + + 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, strlen(s) + 1); + + } + + 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/source4/rpc_client/cli_wkssvc.c b/source4/rpc_client/cli_wkssvc.c new file mode 100644 index 0000000000..97b948bf62 --- /dev/null +++ b/source4/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/source4/rpc_parse/.cvsignore b/source4/rpc_parse/.cvsignore new file mode 100644 index 0000000000..5f2a5c4cf7 --- /dev/null +++ b/source4/rpc_parse/.cvsignore @@ -0,0 +1,2 @@ +*.po +*.po32 diff --git a/source4/rpc_parse/parse_dfs.c b/source4/rpc_parse/parse_dfs.c new file mode 100644 index 0000000000..6f13500359 --- /dev/null +++ b/source4/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, strlen(entrypath)+1); + init_unistr2(&q_d->ServerName, servername, strlen(servername)+1); + init_unistr2(&q_d->ShareName, sharename, strlen(sharename)+1); + 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, strlen(entrypath)+1); + init_unistr2(&q_d->ServerName, servername, strlen(servername)+1); + init_unistr2(&q_d->ShareName, sharename, strlen(sharename)+1); + if(comment != NULL) { + init_unistr2(&q_d->Comment, comment, strlen(comment)+1); + 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, strlen(entrypath)+1); + init_unistr2(&q_d->uni_server, servername, strlen(servername)+1); + init_unistr2(&q_d->uni_share, sharename, strlen(sharename)+1); + 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;idfs.info1[i].ptr_entrypath)) + return False; + } + for(i=0;idfs.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;idfs.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;idfs.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;idfs.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;idfs.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;inum_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;inum_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/source4/rpc_parse/parse_ds.c b/source4/rpc_parse/parse_ds.c new file mode 100644 index 0000000000..ab07631831 --- /dev/null +++ b/source4/rpc_parse/parse_ds.c @@ -0,0 +1,122 @@ +/* + * 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. + */ + +#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 ( !prs_uint8s(False, "domain_guid", ps, depth, p->domain_guid.info, GUID_SIZE) ) + 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, DS_Q_GETPRIMDOMINFO *q_u, prs_struct *ps, int depth) +{ + 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, DS_R_GETPRIMDOMINFO *r_u, prs_struct *ps, int depth) +{ + 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; +} diff --git a/source4/rpc_parse/parse_lsa.c b/source4/rpc_parse/parse_lsa.c new file mode 100644 index 0000000000..53a0fc958d --- /dev/null +++ b/source4/rpc_parse/parse_lsa.c @@ -0,0 +1,2525 @@ +/* + * 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 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) +{ + int len_name = strlen(name); + + if(len_name == 0) + len_name = 1; + + trn->sid_name_use = sid_name_use; + init_uni_hdr(&trn->hdr_name, len_name); + init_unistr2(uni_name, name, len_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) +{ + 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, + strlen(server_name) + 1); + + 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) +{ + 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_uni_hdr2(&r_e->hdr_domain_name[i], strlen_w((td[i])->name)); + init_dom_sid2(&r_e->domain_sid[i], &(td[i])->sid); + + init_unistr2_w(ctx, &r_e->uni_domain_name[i], (td[i])->name); + + }; + } + +} + +/******************************************************************* + 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, 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) +{ + 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, DOM_SID *sids, + uint16 level) +{ + DEBUG(5, ("init_r_enum_trust_dom\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) +{ + 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) +{ + 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++) { + int len; + len = strlen(names[i]); + + init_uni_hdr(&q_l->hdr_name[i], len); + init_unistr2(&q_l->uni_name[i], names[i], len); + } +} + +/******************************************************************* +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) +{ + 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) +{ + 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) +{ + int len_name = strlen(name); + + if(len_name == 0) + len_name = 1; + + memcpy(&trn->pol, hnd, sizeof(trn->pol)); + + init_uni_hdr(&trn->hdr_name, len_name); + init_unistr2(&trn->name, name, len_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_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; icount; i++) { + if (!lsa_io_luid_attr(desc, &r_c->set[i], ps, depth)) + return False; + } + + return True; +} + +void init_lsa_r_enum_privsaccount(LSA_R_ENUMPRIVSACCOUNT *r_u, LUID_ATTR *set, uint32 count, uint32 control) +{ + r_u->ptr=1; + r_u->count=count; + r_u->set.set=set; + r_u->set.count=count; + r_u->set.control=control; + DEBUG(10,("init_lsa_r_enum_privsaccount: %d %d privileges\n", r_u->count, r_u->set.count)); +} + +/******************************************************************* + 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 (!(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) +{ + int len_name = strlen(name); + memcpy(&trn->pol, hnd, sizeof(trn->pol)); + + if(len_name == 0) + len_name = 1; + + init_uni_hdr(&trn->hdr_right, len_name); + init_unistr2(&trn->uni2_right, name, len_name); +} + +/******************************************************************* + 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 (!(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 (!(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 (!prs_uint8s(False, "dom_guid", ps, depth, info->dom_guid.info, GUID_SIZE)) + 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_R_ENUM_ACCT_RIGHTS structure. +********************************************************************/ +void init_r_enum_acct_rights(LSA_R_ENUM_ACCT_RIGHTS *q_r, + uint32 count, + const char **rights) +{ + DEBUG(5, ("init_r_enum_acct_rights\n")); + + q_r->count = count; + init_unistr2_array(&q_r->rights, count, rights); +} + + +/******************************************************************* + 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); +} + + +/******************************************************************* +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_R_ADD_ACCT_RIGHTS structure. +********************************************************************/ +void init_r_add_acct_rights(LSA_R_ADD_ACCT_RIGHTS *q_r) +{ + DEBUG(5, ("init_r_add_acct_rights\n")); + /* oh what a silly function! */ +} + + +/******************************************************************* + 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); +} + + +/******************************************************************* +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_REMOVE_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; +} + +/******************************************************************* + Inits an LSA_R_REMOVE_ACCT_RIGHTS structure. +********************************************************************/ +void init_r_remove_acct_rights(LSA_R_REMOVE_ACCT_RIGHTS *q_r) +{ + DEBUG(5, ("init_r_remove_acct_rights\n")); +} + +/******************************************************************* + Inits an LSA_Q_ENUM_ACCT_WITH_RIGHT structure. +********************************************************************/ +void init_q_enum_acct_with_right(LSA_Q_ENUM_ACCT_WITH_RIGHT *q_q, + POLICY_HND *hnd, + const char *right) +{ + DEBUG(5, ("init_q_enum_acct_with_right\n")); + + q_q->pol = *hnd; + init_unistr2(&q_q->right, right, strlen(right)); + init_str_hdr(&q_q->right_hdr, + q_q->right.uni_max_len*2, + q_q->right.uni_max_len*2, right?1:0); +} + + +/******************************************************************* +reads or writes a LSA_Q_ENUM_ACCT_WITH_RIGHT structure. +********************************************************************/ +BOOL lsa_io_q_enum_acct_with_right(const char *desc, LSA_Q_ENUM_ACCT_WITH_RIGHT *q_q, prs_struct *ps, int depth) +{ + prs_debug(ps, depth, desc, "lsa_io_q_enum_acct_with_right"); + depth++; + + if (!smb_io_pol_hnd("", &q_q->pol, ps, depth)) + return False; + + if (!prs_uint32("ref_id ", ps, depth, &q_q->right_hdr.buffer)) + return False; + + if (UNMARSHALLING(ps) && q_q->right_hdr.buffer == 0) { + return True; + } + + if (!smb_io_strhdr("", &q_q->right_hdr, ps, depth)) + return False; + + if (!smb_io_unistr2("", &q_q->right, q_q->right_hdr.buffer, ps, depth)) + return False; + + return True; +} + + +/******************************************************************* +reads or writes a LSA_R_ENUM_ACCT_WITH_RIGHT structure. +********************************************************************/ +BOOL lsa_io_r_enum_acct_with_right(const char *desc, LSA_R_ENUM_ACCT_WITH_RIGHT *r_c, prs_struct *ps, int depth) +{ + prs_debug(ps, depth, desc, "lsa_io_r_enum_acct_with_right"); + depth++; + + if (!prs_uint32("count ", ps, depth, &r_c->count)) + return False; + + if (!smb_io_sid_array("sids ", &r_c->sids, ps, depth)) + return False; + + if(!prs_ntstatus("status", ps, depth, &r_c->status)) + return False; + + return True; +} + +/******************************************************************* + Inits an LSA_R_ENUM_ACCT_WITH_RIGHT structure. +********************************************************************/ +void init_r_enum_acct_with_right(LSA_R_ENUM_ACCT_WITH_RIGHT *r_c, + uint32 count, + DOM_SID *sids) +{ + DEBUG(5, ("init_r_enum_acct_with_right\n")); + + r_c->count = count; + init_sid_array(&r_c->sids, count, sids); +} diff --git a/source4/rpc_parse/parse_misc.c b/source4/rpc_parse/parse_misc.c new file mode 100644 index 0000000000..ad50c4c6d1 --- /dev/null +++ b/source4/rpc_parse/parse_misc.c @@ -0,0 +1,1784 @@ +/* + * 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; + +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_freeREWRITE(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; +} + +/******************************************************************* +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, int len) +{ + hdr->uni_str_len = 2 * len; + hdr->uni_max_len = 2 * len; + hdr->buffer = 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, int len) +{ + init_uni_hdr(&hdr->unihdr, len); + hdr->buffer = (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, int len) +{ + ZERO_STRUCTP(str); + + /* max buffer size (allocated size) */ + str->buf_max_len = len; + str->undoc = 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("undoc ", ps, depth, &buf2->undoc)) + 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, strlen(buf)+1); + + } else { + + *ptr = 0; + init_unistr2(str, "", 0); + + } +} + +/******************************************************************* + Copies a UNISTR2 structure. +********************************************************************/ + +void copy_unistr2(UNISTR2 *str, const UNISTR2 *from) +{ + + /* set up string lengths. add one if string is not null-terminated */ + str->uni_max_len = from->uni_max_len; + str->undoc = from->undoc; + 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->undoc = 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("undoc ", ps, depth, &str2->undoc)) + 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, size_t len) +{ + ZERO_STRUCTP(str); + + /* set up string lengths. */ + str->uni_max_len = (uint32)len; + str->undoc = 0; + str->uni_str_len = (uint32)len; + + 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; + } + + /* + * don't move this test above ! The UNISTR2 must be initialized !!! + * jfm, 7/7/2001. + */ + if (buf==NULL) + return; + + rpcstr_push((char *)str->buffer, buf, len, STR_TERMINATE); +} + +/** + * 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->undoc = 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->undoc = 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; +} + + +/******************************************************************* + 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("undoc ", ps, depth, &uni2->undoc)) + 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) +{ + 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;istrings[i].string, strings[i], strlen(strings[i])); + 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) +{ + 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;icount;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;icount;i++) { + if (! smb_io_unistr2("string", &array->strings[i].string, array->strings[i].ref_id, ps, depth)) + return False; + } + + return True; +} + + +/* + initialise a SID_ARRAY from a list of sids +*/ +BOOL init_sid_array(SID_ARRAY *array, + uint32 count, DOM_SID *sids) +{ + int i; + + array->count = count; + array->ref_id = count?1:0; + if (array->count == 0) { + return True; + } + + array->sids = (SID_ARRAY_EL *)talloc_zero(get_talloc_ctx(), count * sizeof(SID_ARRAY_EL)); + if (!array->sids) { + return False; + } + + for (i=0;isids[i].ref_id = 1; + init_dom_sid2(&array->sids[i].sid, &sids[i]); + } + + return True; +} + + +/******************************************************************* + Reads or writes a SID_ARRAY structure. +********************************************************************/ +BOOL smb_io_sid_array(const char *desc, SID_ARRAY *array, prs_struct *ps, int depth) +{ + int i; + + prs_debug(ps, depth, desc, "smb_io_sid_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->sids = talloc_zero(get_talloc_ctx(), array->count * sizeof(array->sids[0])); + } + if (! array->sids) { + return False; + } + + for (i=0;icount;i++) { + if(!prs_uint32("ref_id", ps, depth, &array->sids[i].ref_id)) + return False; + } + + for (i=0;icount;i++) { + if (!smb_io_dom_sid2("sid", &array->sids[i].sid, 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 *logp, const char *logon_srv, const char *comp_name) +{ + DEBUG(5,("init_clnt_srv: %d\n", __LINE__)); + + if (logon_srv != NULL) { + logp->undoc_buffer = 1; + init_unistr2(&logp->uni_logon_srv, logon_srv, strlen(logon_srv)+1); + } else { + logp->undoc_buffer = 0; + } + + if (comp_name != NULL) { + logp->undoc_buffer2 = 1; + init_unistr2(&logp->uni_comp_name, comp_name, strlen(comp_name)+1); + } else { + logp->undoc_buffer2 = 0; + } +} + +/******************************************************************* + Inits or writes a DOM_CLNT_SRV structure. +********************************************************************/ + +static BOOL smb_io_clnt_srv(const char *desc, DOM_CLNT_SRV *logp, prs_struct *ps, int depth) +{ + if (logp == 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, &logp->undoc_buffer)) + return False; + + if (logp->undoc_buffer != 0) { + if(!smb_io_unistr2("unistr2", &logp->uni_logon_srv, logp->undoc_buffer, ps, depth)) + return False; + } + + if(!prs_align(ps)) + return False; + + if(!prs_uint32("undoc_buffer2", ps, depth, &logp->undoc_buffer2)) + return False; + + if (logp->undoc_buffer2 != 0) { + if(!smb_io_unistr2("unistr2", &logp->uni_comp_name, logp->undoc_buffer2, ps, depth)) + return False; + } + + return True; +} + +/******************************************************************* + Inits a DOM_LOG_INFO structure. +********************************************************************/ + +void init_log_info(DOM_LOG_INFO *logp, const char *logon_srv, const char *acct_name, + uint16 sec_chan, const char *comp_name) +{ + DEBUG(5,("make_log_info %d\n", __LINE__)); + + logp->undoc_buffer = 1; + + init_unistr2(&logp->uni_logon_srv, logon_srv, strlen(logon_srv)+1); + init_unistr2(&logp->uni_acct_name, acct_name, strlen(acct_name)+1); + + logp->sec_chan = sec_chan; + + init_unistr2(&logp->uni_comp_name, comp_name, strlen(comp_name)+1); +} + +/******************************************************************* + Reads or writes a DOM_LOG_INFO structure. +********************************************************************/ + +BOOL smb_io_log_info(const char *desc, DOM_LOG_INFO *logp, prs_struct *ps, int depth) +{ + if (logp == 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, &logp->undoc_buffer)) + return False; + + if(!smb_io_unistr2("unistr2", &logp->uni_logon_srv, True, ps, depth)) + return False; + if(!smb_io_unistr2("unistr2", &logp->uni_acct_name, True, ps, depth)) + return False; + + if(!prs_uint16("sec_chan", ps, depth, &logp->sec_chan)) + return False; + + if(!smb_io_unistr2("unistr2", &logp->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 *logp, uint32 log_id_low, uint32 log_id_high) +{ + DEBUG(5,("make_logon_id: %d\n", __LINE__)); + + logp->low = log_id_low; + logp->high = log_id_high; +} + +/******************************************************************* + Reads or writes a DOM_LOGON_ID structure. +********************************************************************/ + +BOOL smb_io_logon_id(const char *desc, DOM_LOGON_ID *logp, prs_struct *ps, int depth) +{ + if (logp == 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, &logp->low )) + return False; + if(!prs_uint32("high", ps, depth, &logp->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/source4/rpc_parse/parse_net.c b/source4/rpc_parse/parse_net.c new file mode 100644 index 0000000000..53f660fcc3 --- /dev/null +++ b/source4/rpc_parse/parse_net.c @@ -0,0 +1,2971 @@ +/* + * 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) +{ + int len_dc_name = strlen(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, len_dc_name+1); + else + init_unistr2(&info->uni_trusted_dc_name, "", 1); +} + +/******************************************************************* + 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, strlen(srv_name) + 1); +} + +/******************************************************************* + 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, strlen(srv_name) + 1); +} + +/******************************************************************* + 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_TRUST_DOM_LIST structure. +********************************************************************/ + +void init_r_trust_dom(NET_R_TRUST_DOM_LIST *r_t, + uint32 num_doms, const char *dom_name) +{ + 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(domain_name); + init_unistr2(&r_t->uni_trust_dom_name[i], domain_name, strlen(domain_name)+1); + /* the use of UNISTR2 here is non-standard. */ + r_t->uni_trust_dom_name[i].undoc = 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 , strlen(logon_srv )+1); + init_unistr2(&q_c->uni_logon_clnt, logon_clnt, strlen(logon_clnt)+1); + + 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, 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]) +{ + int len_domain_name = strlen(domain_name); + int len_user_name = strlen(user_name ); + int len_wksta_name = strlen(wksta_name ); + + unsigned char lm_owf[16]; + unsigned char nt_owf[16]; + + DEBUG(5,("init_id_info1: %d\n", __LINE__)); + + id->ptr_id_info1 = 1; + + init_uni_hdr(&id->hdr_domain_name, len_domain_name); + + id->param_ctrl = param_ctrl; + init_logon_id(&id->logon_id, log_id_low, log_id_high); + + init_uni_hdr(&id->hdr_user_name, len_user_name); + init_uni_hdr(&id->hdr_wksta_name, len_wksta_name); + + 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, len_domain_name); + init_unistr2(&id->uni_user_name, user_name, len_user_name); + init_unistr2(&id->uni_wksta_name, wksta_name, len_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, int lm_chal_resp_len, + const uchar * nt_chal_resp, int nt_chal_resp_len) +{ + int len_domain_name = strlen(domain_name); + int len_user_name = strlen(user_name ); + int len_wksta_name = strlen(wksta_name ); + unsigned char lm_owf[24]; + unsigned char nt_owf[128]; + + DEBUG(5,("init_id_info2: %d\n", __LINE__)); + + id->ptr_id_info2 = 1; + + init_uni_hdr(&id->hdr_domain_name, len_domain_name); + + id->param_ctrl = param_ctrl; + init_logon_id(&id->logon_id, log_id_low, log_id_high); + + init_uni_hdr(&id->hdr_user_name, len_user_name); + init_uni_hdr(&id->hdr_wksta_name, len_wksta_name); + + 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, len_domain_name); + init_unistr2(&id->uni_user_name, user_name, len_user_name); + init_unistr2(&id->uni_wksta_name, wksta_name, len_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 sess_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 */ + 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; + + int len_user_name, len_full_name, len_home_dir, + len_dir_drive, len_logon_script, len_profile_path; + + int len_logon_srv = strlen(logon_srv); + int len_logon_dom = strlen(logon_dom); + + len_user_name = strlen(user_name ); + len_full_name = strlen(full_name ); + len_home_dir = strlen(home_dir ); + len_dir_drive = strlen(dir_drive ); + len_logon_script = strlen(logon_script); + len_profile_path = strlen(profile_path); + + + 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; + + init_uni_hdr(&usr->hdr_user_name, len_user_name); + init_uni_hdr(&usr->hdr_full_name, len_full_name); + init_uni_hdr(&usr->hdr_logon_script, len_logon_script); + init_uni_hdr(&usr->hdr_profile_path, len_profile_path); + init_uni_hdr(&usr->hdr_home_dir, len_home_dir); + init_uni_hdr(&usr->hdr_dir_drive, len_dir_drive); + + 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 (sess_key != NULL) + memcpy(usr->user_sess_key, sess_key, sizeof(usr->user_sess_key)); + else + memset((char *)usr->user_sess_key, '\0', sizeof(usr->user_sess_key)); + + init_uni_hdr(&usr->hdr_logon_srv, len_logon_srv); + init_uni_hdr(&usr->hdr_logon_dom, len_logon_dom); + + 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)); + + 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, len_user_name); + init_unistr2(&usr->uni_full_name, full_name, len_full_name); + init_unistr2(&usr->uni_logon_script, logon_script, len_logon_script); + init_unistr2(&usr->uni_profile_path, profile_path, len_profile_path); + init_unistr2(&usr->uni_home_dir, home_dir, len_home_dir); + init_unistr2(&usr->uni_dir_drive, dir_drive, len_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, len_logon_srv); + init_unistr2(&usr->uni_logon_dom, logon_dom, len_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) +{ + 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_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, strlen(srv_name) + 1); + init_unistr2(&q_s->uni_cli_name, cli_name, strlen(cli_name) + 1); + + 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 (prs_offset(ps) + 40 > prs_data_size(ps)) + return False; + prs_set_offset(ps, prs_offset(ps) + 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 (prs_offset(ps) + 48 > prs_data_size(ps)) + return False; + prs_set_offset(ps, prs_offset(ps) + 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 = prs_offset(ps); + if (len == 0x44) + { + if (ps->io) + { + /* reading */ + if (!prs_hash1(ps, prs_offset(ps), sess_key)) + 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)) + return False; + } + } + if (old_offset + len > prs_data_size(ps)) + return False; + prs_set_offset(ps, 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 (prs_offset(ps) + 16 > prs_data_size(ps)) + return False; + prs_set_offset(ps, prs_offset(ps) + 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 (prs_offset(ps) + 40 > prs_data_size(ps)) + return False; + prs_set_offset(ps, prs_offset(ps) + 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 (!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 (info->ptr_members != 0) + { + if (prs_offset(ps) + 16 > prs_data_size(ps)) + return False; + prs_set_offset(ps, prs_offset(ps) + 16); + + 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) +{ + 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; inum_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; +} + +/******************************************************************* +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; +} + +/******************************************************************* +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; +} + +/******************************************************************* +reads or writes a structure. +********************************************************************/ +static BOOL net_io_sam_privs_info(const char *desc, SAM_DELTA_PRIVS *info, + prs_struct *ps, int depth) +{ + 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; iattribute_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; iprivlist_count; i++) + if(!smb_io_unihdr("hdr_privslist", &info->hdr_privslist[i], ps, depth)) + return False; + + for (i=0; iprivlist_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; + + case SAM_DELTA_TRUST_DOMS: + if (!net_io_sam_trustdoms_info("", &delta->trustdoms_info, ps, depth)) + return False; + break; + + case SAM_DELTA_SECRET_INFO: + if (!net_io_sam_secret_info("", &delta->secret_info, ps, depth)) + return False; + 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, strlen(srv_name) + 1); + init_unistr2(&q_s->uni_cli_name, cli_name, strlen(cli_name) + 1); + + 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) +{ + 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/source4/rpc_parse/parse_prs.c b/source4/rpc_parse/parse_prs.c new file mode 100644 index 0000000000..46879de681 --- /dev/null +++ b/source4/rpc_parse/parse_prs.c @@ -0,0 +1,1329 @@ +/* + 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. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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", DEBUGTAB(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", DEBUGTAB(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", DEBUGTAB(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", DEBUGTAB(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", DEBUGTAB(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)); + } + + 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: ", DEBUGTAB(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: ", DEBUGTAB(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: ", DEBUGTAB(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: ", DEBUGTAB(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) +{ + 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: ", DEBUGTAB(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) +{ + 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: ", DEBUGTAB(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: ", DEBUGTAB(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 len, int max_buf_size) +{ + char *q; + int i; + + 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; + + ZERO_STRUCTP(ps); + 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]) +{ + 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, 68); +#endif + SamOEMhash((uchar *) q, sess_key, 68); + +#ifdef DEBUG_PASSWORD + dump_data(100, q, 68); +#endif + + return True; +} diff --git a/source4/rpc_parse/parse_reg.c b/source4/rpc_parse/parse_reg.c new file mode 100644 index 0000000000..b4d20bf2ba --- /dev/null +++ b/source4/rpc_parse/parse_reg.c @@ -0,0 +1,1872 @@ +/* + * 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, (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) +{ + int len_name = name != NULL ? strlen(name ) + 1: 0; + int len_class = class != NULL ? strlen(class) + 1: 0; + + ZERO_STRUCTP(q_c); + + memcpy(&q_c->pnt_pol, hnd, sizeof(q_c->pnt_pol)); + + init_uni_hdr(&q_c->hdr_name, len_name); + init_unistr2(&q_c->uni_name, name, len_name); + + init_uni_hdr(&q_c->hdr_class, len_class); + init_unistr2(&q_c->uni_class, class, len_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) +{ + int len_name = name != NULL ? strlen(name ) + 1: 0; + ZERO_STRUCTP(q_c); + + memcpy(&q_c->pnt_pol, hnd, sizeof(q_c->pnt_pol)); + + init_uni_hdr(&q_c->hdr_name, len_name); + init_unistr2(&q_c->uni_name, name, len_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) +{ + int len_name = name != NULL ? strlen(name ) + 1: 0; + ZERO_STRUCTP(q_c); + + memcpy(&q_c->pnt_pol, hnd, sizeof(q_c->pnt_pol)); + + init_uni_hdr(&q_c->hdr_name, len_name); + init_unistr2(&q_c->uni_name, name, len_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, + uint32 max_class_len) +{ + ZERO_STRUCTP(q_o); + + memcpy(&q_o->pol, hnd, sizeof(q_o->pol)); + init_uni_hdr(&q_o->hdr_class, max_class_len); + q_o->uni_class.uni_max_len = max_class_len; +} + +/******************************************************************* +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) +{ + int len_type = val_name != NULL ? strlen(val_name) + 1 : 0; + + if (q_i == NULL) + return False; + + q_i->pol = *pol; + + init_uni_hdr(&(q_i->hdr_type), len_type); + init_unistr2(&(q_i->uni_type), val_name, len_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, uint32 max_val_len, + 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, max_val_len); + q_i->uni_name.uni_max_len = max_val_len; + + 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_uni_hdr( &r_u->hdr_name, strlen(val->valuename)+1 ); + init_unistr2( &r_u->uni_name, val->valuename, strlen(val->valuename)+1 ); + + /* 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) +{ + int val_len = strlen(val_name) + 1; + + ZERO_STRUCTP(q_i); + + memcpy(&q_i->pol, pol, sizeof(q_i->pol)); + + init_uni_hdr(&q_i->hdr_name, val_len); + init_unistr2(&q_i->uni_name, val_name, val_len); + + 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) +{ + int len_name = strlen(key_name)+1; + + memcpy(&r_q->pol, pol, sizeof(r_q->pol)); + + init_uni_hdr(&r_q->hdr_name, len_name); + init_unistr2(&r_q->uni_name, key_name, len_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) +{ + int msg_len; + msg_len = strlen(msg); + + q_s->ptr_0 = 1; + q_s->ptr_1 = 1; + q_s->ptr_2 = 1; + + init_uni_hdr(&(q_s->hdr_msg), msg_len); + init_unistr2(&(q_s->uni_msg), msg, msg_len); + + 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/source4/rpc_parse/parse_rpc.c b/source4/rpc_parse/parse_rpc.c new file mode 100644 index 0000000000..fafbbb1965 --- /dev/null +++ b/source4/rpc_parse/parse_rpc.c @@ -0,0 +1,1106 @@ +/* + * 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 \ +} + +/* + * 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 }, + { 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(!prs_uint32 ("data ", ps, depth, &ifc->uuid.time_low)) + return False; + if(!prs_uint16 ("data ", ps, depth, &ifc->uuid.time_mid)) + return False; + if(!prs_uint16 ("data ", ps, depth, &ifc->uuid.time_hi_and_version)) + return False; + + if(!prs_uint8s (False, "data ", ps, depth, ifc->uuid.remaining, sizeof(ifc->uuid.remaining))) + 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; +} + +/******************************************************************* + Checks an RPC_HDR_AUTH structure. +********************************************************************/ + +BOOL rpc_hdr_auth_chk(RPC_HDR_AUTH *rai) +{ + return (rai->auth_type == NTLMSSP_AUTH_TYPE && rai->auth_level == NTLMSSP_AUTH_LEVEL); +} + +/******************************************************************* + Inits an RPC_HDR_AUTH structure. +********************************************************************/ + +void init_rpc_hdr_auth(RPC_HDR_AUTH *rai, + uint8 auth_type, uint8 auth_level, + uint8 stub_type_len, + uint32 ptr) +{ + 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 = 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 ("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; +} + +/******************************************************************* + 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, strlen("NTLMSSP"), + sizeof(rav->signature))) + return False; + if(!prs_uint32("msg_type ", ps, depth, &rav->msg_type)) /* NTLMSSP_MESSAGE_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", + crc32, NTLMSSP_SIGN_VERSION, seq_num)); + DEBUG(5,("verify expect - crc %x ver %x seq %d\n", + chk->crc32, chk->ver, chk->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; +} diff --git a/source4/rpc_parse/parse_samr.c b/source4/rpc_parse/parse_samr.c new file mode 100644 index 0000000000..d031d13955 --- /dev/null +++ b/source4/rpc_parse/parse_samr.c @@ -0,0 +1,7448 @@ +/* + * 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) Anthony Liguori 2002, + * Copyright (C) Jim McDonough 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) +{ + int len_name = strlen(dom_name); + + DEBUG(5, ("init_samr_q_lookup_domain\n")); + + q_u->connect_pol = *pol; + + init_uni_hdr(&q_u->hdr_domain, len_name); + init_unistr2(&q_u->uni_domain, dom_name, len_name); +} + +/******************************************************************* +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_unknown_2d(SAMR_Q_UNKNOWN_2D * q_u, POLICY_HND *dom_pol, DOM_SID *sid) +{ + DEBUG(5, ("samr_init_samr_q_unknown_2d\n")); + + q_u->dom_pol = *dom_pol; + init_dom_sid2(&q_u->sid, sid); +} + +/******************************************************************* +reads or writes a structure. +********************************************************************/ + +BOOL samr_io_q_unknown_2d(const char *desc, SAMR_Q_UNKNOWN_2D * q_u, + prs_struct *ps, int depth) +{ + if (q_u == NULL) + return False; + + prs_debug(ps, depth, desc, "samr_io_q_unknown_2d"); + 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_unknown_2d(const char *desc, SAMR_R_UNKNOWN_2D * r_u, + prs_struct *ps, int depth) +{ + if (r_u == NULL) + return False; + + prs_debug(ps, depth, desc, "samr_io_r_unknown_2d"); + 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) +{ + int len_server = strlen(server); + + init_uni_hdr(&u_5->hdr_server, len_server); + + init_unistr2(&u_5->uni_server, server, len_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) +{ + int len_domain = strlen(domain); + int len_server = strlen(server); + + u_2->unknown_0 = 0x00000000; + u_2->unknown_1 = 0x80000000; + u_2->unknown_2 = 0x00000000; + + u_2->ptr_0 = 1; + init_uni_hdr(&u_2->hdr_domain, len_domain); + init_uni_hdr(&u_2->hdr_server, len_server); + + 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, len_domain); + init_unistr2(&u_2->uni_server, server, len_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, + uint32 len_sam_name, uint32 len_sam_full, + uint32 len_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, len_sam_name); + init_uni_hdr(&sam->hdr_user_name, len_sam_full); + init_uni_hdr(&sam->hdr_user_desc, len_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, + uint32 len_sam_name, uint32 len_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, len_sam_name); + init_uni_hdr(&sam->hdr_srv_desc, len_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, + uint32 len_grp_name, uint32 len_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, len_grp_name); + init_uni_hdr(&sam->hdr_grp_desc, len_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, uint32 len_sam_name, uint32 rid) +{ + DEBUG(10, ("init_sam_entry: %d %d\n", len_sam_name, rid)); + + sam->rid = rid; + init_uni_hdr(&sam->hdr_name, len_sam_name); +} + +/******************************************************************* +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, DISP_USER_INFO *disp_user_info, + DOM_SID *domain_sid) +{ + uint32 len_sam_name, len_sam_full, len_sam_desc; + 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].sam; + + 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; + } + + len_sam_name = strlen(username); + len_sam_full = strlen(fullname); + len_sam_desc = strlen(acct_desc); + + init_sam_entry1(&sam->sam[i], start_idx + i + 1, + len_sam_name, len_sam_full, len_sam_desc, + user_rid, pdb_get_acct_ctrl(pwd)); + + ZERO_STRUCTP(&sam->str[i].uni_acct_name); + ZERO_STRUCTP(&sam->str[i].uni_full_name); + ZERO_STRUCTP(&sam->str[i].uni_acct_desc); + + init_unistr2(&sam->str[i].uni_acct_name, pdb_get_username(pwd), len_sam_name); + init_unistr2(&sam->str[i].uni_full_name, pdb_get_fullname(pwd), len_sam_full); + init_unistr2(&sam->str[i].uni_acct_desc, pdb_get_acct_desc(pwd), len_sam_desc); + } + + 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, DISP_USER_INFO *disp_user_info, + DOM_SID *domain_sid ) +{ + uint32 len_sam_name, len_sam_desc; + 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].sam; + + 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; + } + + len_sam_name = strlen(username); + len_sam_desc = strlen(acct_desc); + + init_sam_entry2(&sam->sam[i], start_idx + i + 1, + len_sam_name, len_sam_desc, + user_rid, pdb_get_acct_ctrl(pwd)); + + ZERO_STRUCTP(&sam->str[i].uni_srv_name); + ZERO_STRUCTP(&sam->str[i].uni_srv_desc); + + init_unistr2(&sam->str[i].uni_srv_name, username, len_sam_name); + init_unistr2(&sam->str[i].uni_srv_desc, pdb_get_acct_desc(pwd), len_sam_desc); + } + + 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, DISP_GROUP_INFO *disp_group_info) +{ + uint32 len_sam_name, len_sam_desc; + 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].grp; + + DEBUG(11, ("init_sam_dispinfo_3: entry: %d\n",i)); + + len_sam_name = strlen(grp->name); + len_sam_desc = strlen(grp->comment); + + init_sam_entry3(&sam->sam[i], start_idx + i + 1, len_sam_name, len_sam_desc, grp->rid); + + init_unistr2(&sam->str[i].uni_grp_name, grp->name, len_sam_name); + init_unistr2(&sam->str[i].uni_grp_desc, grp->comment, len_sam_desc); + } + + 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, DISP_USER_INFO *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].sam; + + 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, DISP_GROUP_INFO *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].grp; + + 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) +{ + int desc_len = acct_desc != NULL ? strlen(acct_desc) : 0; + int acct_len = acct_name != NULL ? strlen(acct_name) : 0; + + DEBUG(5, ("init_samr_group_info1\n")); + + init_uni_hdr(&gr1->hdr_acct_name, acct_len); + + gr1->unknown_1 = 0x3; + gr1->num_members = num_members; + + init_uni_hdr(&gr1->hdr_acct_desc, desc_len); + + init_unistr2(&gr1->uni_acct_name, acct_name, acct_len); + init_unistr2(&gr1->uni_acct_desc, acct_desc, desc_len); +} + +/******************************************************************* +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, char *acct_desc) +{ + int acct_len = acct_desc != NULL ? strlen(acct_desc) : 0; + + DEBUG(5, ("init_samr_group_info4\n")); + + init_uni_hdr(&gr4->hdr_acct_desc, acct_len); + init_unistr2(&gr4->uni_acct_desc, acct_desc, acct_len); +} + +/******************************************************************* +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_align(ps)) + 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, char *acct_desc, + uint32 access_mask) +{ + int acct_len = acct_desc != NULL ? strlen(acct_desc) : 0; + + DEBUG(5, ("init_samr_q_create_dom_group\n")); + + q_e->pol = *pol; + + init_uni_hdr(&q_e->hdr_acct_desc, acct_len); + init_unistr2(&q_e->uni_acct_desc, acct_desc, acct_len); + + 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) +{ + int acct_len_name = acct_name != NULL ? strlen(acct_name) : 0; + int acct_len_desc = acct_desc != NULL ? strlen(acct_desc) : 0; + + DEBUG(5, ("init_samr_alias_info1\n")); + + init_uni_hdr(&al1->hdr_acct_name, acct_len_name); + init_unistr2(&al1->uni_acct_name, acct_name, acct_len_name); + + al1->num_member=num_member; + + init_uni_hdr(&al1->hdr_acct_desc, acct_len_desc); + init_unistr2(&al1->uni_acct_desc, acct_desc, acct_len_desc); +} + +/******************************************************************* +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, char *acct_desc) +{ + int acct_len = acct_desc != NULL ? strlen(acct_desc) : 0; + + DEBUG(5, ("init_samr_alias_info3\n")); + + init_uni_hdr(&al3->hdr_acct_desc, acct_len); + init_unistr2(&al3->uni_acct_desc, acct_desc, acct_len); +} + +/******************************************************************* +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, char *acct_desc) +{ + int acct_len = acct_desc != NULL ? strlen(acct_desc) : 0; + + DEBUG(5, ("init_samr_q_create_dom_alias\n")); + + q_u->dom_pol = *hnd; + + init_uni_hdr(&q_u->hdr_acct_desc, acct_len); + init_unistr2(&q_u->uni_acct_desc, acct_desc, acct_len); + + q_u->access_mask = 0x001f000f; +} + +/******************************************************************* +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; + uint32 ptr_sid[MAX_LOOKUP_SIDS]; + + 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) { + SMB_ASSERT_ARRAY(ptr_sid, r_u->num_sids); + + if (r_u->num_sids != 0) { + if(!prs_uint32("num_sids1", ps, depth, &r_u->num_sids1)) + return False; + + for (i = 0; i < r_u->num_sids1; i++) { + ptr_sid[i] = 1; + if(!prs_uint32("", ps, depth, &ptr_sid[i])) + return False; + } + + for (i = 0; i < r_u->num_sids1; i++) { + if (ptr_sid[i] != 0) { + if(!smb_io_dom_sid2("", &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++) { + int len_name = name[i] != NULL ? strlen(name[i]) : 0; + init_uni_hdr(&q_u->hdr_name[i], len_name); /* unicode header for user_name */ + init_unistr2(&q_u->uni_name[i], name[i], len_name); /* unicode string for machine account */ + } + + 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) +{ + int len_name; + len_name = strlen(name); + + DEBUG(5, ("samr_init_samr_q_create_user\n")); + + q_u->domain_pol = *pol; + + init_uni_hdr(&q_u->hdr_name, len_name); + init_unistr2(&q_u->uni_name, name, len_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) +{ + int len_mach_acct; + + DEBUG(5, ("init_sam_user_info11\n")); + + len_mach_acct = strlen(mach_acct); + + memcpy(&(usr->expiry), expiry, sizeof(usr->expiry)); /* expiry time or something? */ + ZERO_STRUCT(usr->padding_1); /* 0 - padding 24 bytes */ + + init_uni_hdr(&usr->hdr_mach_acct, len_mach_acct); /* unicode header for machine account */ + 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, len_mach_acct); /* unicode string 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 + + unknown_3 = 0x09f8 27fa + unknown_5 = 0x0001 0000 + unknown_6 = 0x0000 04ec + + *************************************************************************/ + +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_3 = 0x09f8 27fa + unknown_5 = 0x0001 0000 + 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 unknown_3, + uint16 logon_divs, + LOGON_HRS * hrs, + uint32 unknown_5, + char newpass[516], uint32 unknown_6) +{ + 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; + + 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 */ + + init_uni_hdr(&usr->hdr_user_name, len_user_name); /* NULL */ + init_uni_hdr(&usr->hdr_full_name, len_full_name); + init_uni_hdr(&usr->hdr_home_dir, len_home_dir); + init_uni_hdr(&usr->hdr_dir_drive, len_dir_drive); + init_uni_hdr(&usr->hdr_logon_script, len_logon_script); + init_uni_hdr(&usr->hdr_profile_path, len_profile_path); + init_uni_hdr(&usr->hdr_acct_desc, len_description); + init_uni_hdr(&usr->hdr_workstations, len_workstations); + init_uni_hdr(&usr->hdr_unknown_str, len_unknown_str); + init_uni_hdr(&usr->hdr_munged_dial, len_munged_dial); + + 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->unknown_3 = unknown_3; /* 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->unknown_5 = unknown_5; /* 0x0001 0000 */ + + memcpy(usr->pass, newpass, sizeof(usr->pass)); + + copy_unistr2(&usr->uni_user_name, user_name); + copy_unistr2(&usr->uni_full_name, full_name); + copy_unistr2(&usr->uni_home_dir, home_dir); + copy_unistr2(&usr->uni_dir_drive, dir_drive); + copy_unistr2(&usr->uni_logon_script, log_scr); + copy_unistr2(&usr->uni_profile_path, prof_path); + copy_unistr2(&usr->uni_acct_desc, desc); + copy_unistr2(&usr->uni_workstations, wkstas); + copy_unistr2(&usr->uni_unknown_str, unk_str); + copy_unistr2(&usr->uni_munged_dial, mung_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_3 = 0x09f8 27fa + unknown_5 = 0x0001 0000 + 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 unknown_3, uint16 logon_divs, + LOGON_HRS * hrs, uint32 unknown_5, + char newpass[516], uint32 unknown_6) +{ + int len_user_name = user_name != NULL ? strlen(user_name) : 0; + int len_full_name = full_name != NULL ? strlen(full_name) : 0; + int len_home_dir = home_dir != NULL ? strlen(home_dir) : 0; + int len_dir_drive = dir_drive != NULL ? strlen(dir_drive) : 0; + int len_logon_script = log_scr != NULL ? strlen(log_scr) : 0; + int len_profile_path = prof_path != NULL ? strlen(prof_path) : 0; + int len_description = desc != NULL ? strlen(desc) : 0; + int len_workstations = wkstas != NULL ? strlen(wkstas) : 0; + int len_unknown_str = unk_str != NULL ? strlen(unk_str) : 0; + int len_munged_dial = mung_dial != NULL ? strlen(mung_dial) : 0; + + 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 */ + + init_uni_hdr(&usr->hdr_user_name, len_user_name); /* NULL */ + init_uni_hdr(&usr->hdr_full_name, len_full_name); + init_uni_hdr(&usr->hdr_home_dir, len_home_dir); + init_uni_hdr(&usr->hdr_dir_drive, len_dir_drive); + init_uni_hdr(&usr->hdr_logon_script, len_logon_script); + init_uni_hdr(&usr->hdr_profile_path, len_profile_path); + init_uni_hdr(&usr->hdr_acct_desc, len_description); + init_uni_hdr(&usr->hdr_workstations, len_workstations); + init_uni_hdr(&usr->hdr_unknown_str, len_unknown_str); + init_uni_hdr(&usr->hdr_munged_dial, len_munged_dial); + + 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->unknown_3 = unknown_3; /* 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->unknown_5 = unknown_5; /* 0x0001 0000 */ + + memcpy(usr->pass, newpass, sizeof(usr->pass)); + + init_unistr2(&usr->uni_user_name, user_name, len_user_name); /* NULL */ + init_unistr2(&usr->uni_full_name, full_name, len_full_name); + init_unistr2(&usr->uni_home_dir, home_dir, len_home_dir); + init_unistr2(&usr->uni_dir_drive, dir_drive, len_dir_drive); + init_unistr2(&usr->uni_logon_script, log_scr, len_logon_script); + init_unistr2(&usr->uni_profile_path, prof_path, len_profile_path); + init_unistr2(&usr->uni_acct_desc, desc, len_description); + init_unistr2(&usr->uni_workstations, wkstas, len_workstations); + init_unistr2(&usr->uni_unknown_str, unk_str, len_unknown_str); + init_unistr2(&usr->uni_munged_dial, mung_dial, len_munged_dial); + + 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("unknown_3 ", ps, depth, &usr->unknown_3)) + 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_uint32("unknown_5 ", ps, depth, &usr->unknown_5)) + 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_3 = 0x00ff ffff + unknown_5 = 0x0002 0000 + 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 unknown_3, + uint16 logon_divs, + LOGON_HRS * hrs, + uint32 unknown_5, uint32 unknown_6) +{ + 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; + + 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; + + init_uni_hdr(&usr->hdr_user_name, len_user_name); + init_uni_hdr(&usr->hdr_full_name, len_full_name); + init_uni_hdr(&usr->hdr_home_dir, len_home_dir); + init_uni_hdr(&usr->hdr_dir_drive, len_dir_drive); + init_uni_hdr(&usr->hdr_logon_script, len_logon_script); + init_uni_hdr(&usr->hdr_profile_path, len_profile_path); + init_uni_hdr(&usr->hdr_acct_desc, len_description); + init_uni_hdr(&usr->hdr_workstations, len_workstations); + init_uni_hdr(&usr->hdr_unknown_str, len_unknown_str); + init_uni_hdr(&usr->hdr_munged_dial, len_munged_dial); + + 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->unknown_3 = unknown_3; /* 0x00ff ffff */ + + usr->logon_divs = logon_divs; /* should be 168 (hours/week) */ + usr->ptr_logon_hrs = hrs ? 1 : 0; + usr->unknown_5 = unknown_5; /* 0x0002 0000 */ + + 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); + copy_unistr2(&usr->uni_full_name, full_name); + copy_unistr2(&usr->uni_home_dir, home_dir); + copy_unistr2(&usr->uni_dir_drive, dir_drive); + copy_unistr2(&usr->uni_logon_script, log_scr); + copy_unistr2(&usr->uni_profile_path, prof_path); + copy_unistr2(&usr->uni_acct_desc, desc); + copy_unistr2(&usr->uni_workstations, wkstas); + copy_unistr2(&usr->uni_unknown_str, unk_str); + copy_unistr2(&usr->uni_munged_dial, mung_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_3 = 0x00ff ffff + unknown_5 = 0x0002 0000 + 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; + + int len_user_name, len_full_name, len_home_dir, + len_dir_drive, len_logon_script, len_profile_path, + len_description, len_workstations, len_unknown_str, + len_munged_dial; + + 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); + + uint32 user_rid; + const DOM_SID *user_sid; + + uint32 group_rid; + const DOM_SID *group_sid; + + len_user_name = user_name != NULL ? strlen(user_name )+1 : 0; + len_full_name = full_name != NULL ? strlen(full_name )+1 : 0; + len_home_dir = home_dir != NULL ? strlen(home_dir )+1 : 0; + len_dir_drive = dir_drive != NULL ? strlen(dir_drive )+1 : 0; + len_logon_script = logon_script != NULL ? strlen(logon_script)+1 : 0; + len_profile_path = profile_path != NULL ? strlen(profile_path)+1 : 0; + len_description = description != NULL ? strlen(description )+1 : 0; + len_workstations = workstations != NULL ? strlen(workstations)+1 : 0; + len_unknown_str = 0; + len_munged_dial = munged_dial != NULL ? strlen(munged_dial )+1 : 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; + + init_uni_hdr(&usr->hdr_user_name, len_user_name); + init_uni_hdr(&usr->hdr_full_name, len_full_name); + init_uni_hdr(&usr->hdr_home_dir, len_home_dir); + init_uni_hdr(&usr->hdr_dir_drive, len_dir_drive); + init_uni_hdr(&usr->hdr_logon_script, len_logon_script); + init_uni_hdr(&usr->hdr_profile_path, len_profile_path); + init_uni_hdr(&usr->hdr_acct_desc, len_description); + init_uni_hdr(&usr->hdr_workstations, len_workstations); + init_uni_hdr(&usr->hdr_unknown_str, len_unknown_str); + init_uni_hdr(&usr->hdr_munged_dial, len_munged_dial); + + 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))); + 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))); + 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 unknown_3 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->unknown_3 = 0x00ffffff; + + usr->logon_divs = pdb_get_logon_divs(pw); + usr->ptr_logon_hrs = pdb_get_hours(pw) ? 1 : 0; + usr->unknown_5 = pdb_get_unknown_5(pw); /* 0x0002 0000 */ + + 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, len_user_name); + init_unistr2(&usr->uni_full_name, full_name, len_full_name); + init_unistr2(&usr->uni_home_dir, home_dir, len_home_dir); + init_unistr2(&usr->uni_dir_drive, dir_drive, len_dir_drive); + init_unistr2(&usr->uni_logon_script, logon_script, len_logon_script); + init_unistr2(&usr->uni_profile_path, profile_path, len_profile_path); + init_unistr2(&usr->uni_acct_desc, description, len_description); + init_unistr2(&usr->uni_workstations, workstations, len_workstations); + init_unistr2(&usr->uni_unknown_str, NULL, len_unknown_str); + init_unistr2(&usr->uni_munged_dial, munged_dial, len_munged_dial); + + 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("unknown_3 ", ps, depth, &usr->unknown_3)) + 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_uint32("unknown_5 ", ps, depth, &usr->unknown_5)) + 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) +{ + int len_munged_dial; + const char* munged_dial = pdb_get_munged_dial(pw); + + len_munged_dial = munged_dial != NULL ? strlen(munged_dial )+1 : 0; + init_uni_hdr(&usr->hdr_munged_dial, len_munged_dial); + init_unistr2(&usr->uni_munged_dial, munged_dial, len_munged_dial); + +} + +/******************************************************************* +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. +********************************************************************/ + +void init_samr_userinfo_ctr(SAM_USERINFO_CTR * ctr, uchar * 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: + SamOEMhash(ctr->info.id24->pass, sess_key, 516); + dump_data(100, (char *)sess_key, 16); + dump_data(100, (char *)ctr->info.id24->pass, 516); + break; + case 0x17: + SamOEMhash(ctr->info.id23->pass, sess_key, 516); + dump_data(100, (char *)sess_key, 16); + 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, unsigned char sess_key[16], + 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, unsigned char sess_key[16], + 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: + SamOEMhash(ctr->info.id12->lm_pwd, sess_key, 16); + SamOEMhash(ctr->info.id12->nt_pwd, sess_key, 16); + dump_data(100, (char *)sess_key, 16); + 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) +{ + int len_srv_name = strlen(srv_name); + + DEBUG(5, ("init_samr_q_connect\n")); + + /* make PDC server name \\server */ + q_u->ptr_srv_name = len_srv_name > 0 ? 1 : 0; + init_unistr2(&q_u->uni_srv_name, srv_name, len_srv_name + 1); + + /* 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) +{ + int len_srv_name = strlen(srv_name); + + DEBUG(5, ("init_samr_q_connect\n")); + + /* make PDC server name \\server */ + q_u->ptr_srv_name = len_srv_name > 0 ? 1 : 0; + init_unistr2(&q_u->uni_srv_name, srv_name, len_srv_name + 1); + + /* 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) +{ + int len_srv_name = strlen(srv_name); + + DEBUG(5, ("init_samr_q_get_dom_pwinfo\n")); + + q_u->ptr = 1; + init_uni_hdr(&q_u->hdr_srv_name, len_srv_name); + init_unistr2(&q_u->uni_srv_name, srv_name, len_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, 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, 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, + char *dest_host, char *user_name, + char nt_newpass[516], + uchar nt_oldhash[16], + char lm_newpass[516], + uchar lm_oldhash[16]) +{ + int len_dest_host = strlen(dest_host); + int len_user_name = strlen(user_name); + + DEBUG(5, ("init_samr_q_chgpasswd_user\n")); + + q_u->ptr_0 = 1; + init_uni_hdr(&q_u->hdr_dest_host, len_dest_host); + init_unistr2(&q_u->uni_dest_host, dest_host, len_dest_host); + init_uni_hdr(&q_u->hdr_user_name, len_user_name); + init_unistr2(&q_u->uni_user_name, user_name, len_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/source4/rpc_parse/parse_sec.c b/source4/rpc_parse/parse_sec.c new file mode 100644 index 0000000000..dbd72e5250 --- /dev/null +++ b/source4/rpc_parse/parse_sec.c @@ -0,0 +1,1028 @@ +/* + * Unix SMB/Netbios implementation. + * Version 1.9. + * RPC Pipe client / server routines + * Copyright (C) Andrew Tridgell 1992-1998, + * Copyright (C) Jeremy R. Allison 1995-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. + */ + +#include "includes.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_RPC_PARSE + +/******************************************************************* + Sets up a SEC_ACCESS structure. +********************************************************************/ + +void init_sec_access(SEC_ACCESS *t, uint32 mask) +{ + t->mask = mask; +} + +/******************************************************************* + 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; +} + +/******************************************************************* + 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, GUID_SIZE); + memcpy(&ace_dest->inh_guid, &ace_src->inh_guid, GUID_SIZE); + 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); +} + +/******************************************************************* + 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 (!prs_uint8s(False, "obj_guid", ps, depth, psa->obj_guid.info, GUID_SIZE)) + return False; + + if (psa->obj_flags & SEC_ACE_OBJECT_INHERITED_PRESENT) + if (!prs_uint8s(False, "inh_guid", ps, depth, psa->inh_guid.info, GUID_SIZE)) + 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; +} + +/******************************************************************* + 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) +{ + 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) +{ + 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, size_t *num, DOM_SID *sid) +{ + int i = 0; + 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; + } +} + +/******************************************************************* + 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); +} + +/******************************************************************* + 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) +{ + 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; +} + +/******************************************************************* + 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_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; +} + +/******************************************************************* + Compares two SEC_ACL structures +********************************************************************/ + +BOOL sec_acl_equal(SEC_ACL *s1, SEC_ACL *s2) +{ + 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; +} + +/******************************************************************* + 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, + owner_sid, group_sid, sacl, dacl, &secdesc_size); + + return_sdb = make_sec_desc_buf(ctx, secdesc_size, psd); + + return(return_sdb); +} + +/******************************************************************* + Tallocs a duplicate SID. +********************************************************************/ + +static DOM_SID *sid_dup_talloc(TALLOC_CTX *ctx, 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; +} + +/******************************************************************* + Creates a SEC_DESC structure +********************************************************************/ + +SEC_DESC *make_sec_desc(TALLOC_CTX *ctx, uint16 revision, + DOM_SID *owner_sid, DOM_SID *grp_sid, + SEC_ACL *sacl, SEC_ACL *dacl, size_t *sd_size) +{ + SEC_DESC *dst; + uint32 offset = 0; + uint32 offset_sid = SEC_DESC_HEADER_SIZE; + uint32 offset_acl = 0; + + *sd_size = 0; + + if(( dst = (SEC_DESC *)talloc_zero(ctx, sizeof(SEC_DESC))) == NULL) + return NULL; + + dst->revision = revision; + dst->type = SEC_DESC_SELF_RELATIVE; + + 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 = 0; + + /* + * Work out the linearization sizes. + */ + if (dst->owner_sid != NULL) { + + if (offset == 0) + offset = SEC_DESC_HEADER_SIZE; + + offset += sid_size(dst->owner_sid); + } + + if (dst->grp_sid != NULL) { + + if (offset == 0) + offset = SEC_DESC_HEADER_SIZE; + + offset += sid_size(dst->grp_sid); + } + + if (dst->sacl != NULL) { + + offset_acl = SEC_DESC_HEADER_SIZE; + + dst->off_sacl = offset_acl; + offset_acl += dst->sacl->size; + offset += dst->sacl->size; + offset_sid += dst->sacl->size; + } + + if (dst->dacl != NULL) { + + if (offset_acl == 0) + offset_acl = SEC_DESC_HEADER_SIZE; + + dst->off_dacl = offset_acl; + offset_acl += dst->dacl->size; + offset += dst->dacl->size; + offset_sid += dst->dacl->size; + } + + *sd_size = (size_t)((offset == 0) ? SEC_DESC_HEADER_SIZE : offset); + + if (dst->owner_sid != NULL) + dst->off_owner_sid = offset_sid; + + /* sid_size() returns 0 if the sid is NULL so this is ok */ + + if (dst->grp_sid != NULL) + dst->off_grp_sid = offset_sid + sid_size(dst->owner_sid); + + return dst; + +error_exit: + + *sd_size = 0; + return NULL; +} + +/******************************************************************* + Duplicate a SEC_DESC structure. +********************************************************************/ + +SEC_DESC *dup_sec_desc( TALLOC_CTX *ctx, SEC_DESC *src) +{ + size_t dummy; + + if(src == NULL) + return NULL; + + return make_sec_desc( ctx, src->revision, + 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, + owner_sid, grp_sid, NULL, dacl, sd_size); +} + +/******************************************************************* + 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; +} + +/******************************************************************* + 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); +} + +/******************************************************************* + 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; +} + +/******************************************************************* + adds 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]->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 SID's permissions at 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 SID from 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]->owner_sid, + psd[0]->grp_sid, psd[0]->sacl, dacl, sd_size))) + return NT_STATUS_UNSUCCESSFUL; + + *psd = sd; + sd = 0; + return NT_STATUS_OK; +} diff --git a/source4/rpc_parse/parse_spoolss.c b/source4/rpc_parse/parse_spoolss.c new file mode 100644 index 0000000000..47737908f0 --- /dev/null +++ b/source4/rpc_parse/parse_spoolss.c @@ -0,0 +1,7751 @@ +/* + * 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;icount2;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;icount;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;icount;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;icount;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;icount;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) +{ + uint32 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, 32)) + 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(10, ("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, strlen(printername)+1); + + 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, strlen(datatype)); +*/ + 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, strlen(clientname)+1); + init_unistr2(&q_u->user_ctr.user1.user_name, user_name, strlen(user_name)+1); + + 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, strlen(srv_name)); + + 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, strlen(clientname)+1); + init_unistr2(&q_u->user_ctr.user1.user_name, user_name, strlen(user_name)+1); + 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, strlen(server)+1); + init_unistr2(&q_u->arch, arch, strlen(arch)+1); + init_unistr2(&q_u->driver, driver, strlen(driver)+1); + + + 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, strlen(valuename) + 1); + 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, strlen(valuename) + 1); + init_unistr2(&q_u->keyname, keyname, strlen(keyname) + 1); + 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 = 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 (has_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->undoc=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, strlen(srv_name)+1); + + 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, strlen(key)+1); + 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, strlen(value)+1); + + q_u->max_len = q_u->real_len = data_size; + q_u->data = 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, strlen(value)+1); + init_unistr2(&q_u->key, key, strlen(key)+1); + + q_u->max_len = q_u->real_len = data_size; + q_u->data = 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, strlen(string)+1); + + 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; isize != 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 = 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, strlen(key)+1); + 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, strlen(keyname)+1); + + 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; isize_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; isize_of_array; i++) { + + if (!prs_unistr("valuename", ps, depth, &ctr->values[i].valuename)) + return False; + + 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, strlen(name)+1); + init_unistr2(&q_u->environment, environment, strlen(environment)+1); + + 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, strlen(form_name) + 1); + + 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, strlen(form) + 1); + 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, strlen(formname) + 1); + 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; + + if (docname) + init_unistr2(&ctr->docinfo.doc_info_1.docname, docname, + strlen(docname) + 1); + + if (outputfile) + init_unistr2(&ctr->docinfo.doc_info_1.outputfile, outputfile, + strlen(outputfile) + 1); + + if (datatype) + init_unistr2(&ctr->docinfo.doc_info_1.datatype, datatype, + strlen(datatype) + 1); + + 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 = 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, strlen(valuename) + 1); + + 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, strlen(value) + 1); + init_unistr2(&q_u->keyname, key, strlen(key) + 1); + + 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, + strlen(localmachine) + 1); + + q_u->printerlocal = printerlocal; + + if (option) + q_u->option_ptr = 1; + + q_u->option = option; + + return True; +} diff --git a/source4/rpc_parse/parse_srv.c b/source4/rpc_parse/parse_srv.c new file mode 100644 index 0000000000..bfa1a13be9 --- /dev/null +++ b/source4/rpc_parse/parse_srv.c @@ -0,0 +1,3590 @@ +/* + * 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")); + + if(net_name) + init_unistr2(&sh0->uni_netname, net_name, strlen(net_name)+1); +} + +/******************************************************************* + 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")); + + if(net_name) + init_unistr2(&sh1->uni_netname, net_name, strlen(net_name)+1); + if(remark) + init_unistr2(&sh1->uni_remark, remark, strlen(remark)+1); +} + +/******************************************************************* + 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")); + + if (net_name) + init_unistr2(&sh2->uni_netname, net_name, strlen(net_name)+1); + if (remark) + init_unistr2(&sh2->uni_remark, remark, strlen(remark)+1); + if (path) + init_unistr2(&sh2->uni_path, path, strlen(path)+1); + if (passwd) + init_unistr2(&sh2->uni_passwd, passwd, strlen(passwd)+1); +} + +/******************************************************************* + 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")); + + if(net_name) + init_unistr2(&sh501->uni_netname, net_name, strlen(net_name)+1); + if(remark) + init_unistr2(&sh501->uni_remark, remark, strlen(remark)+1); +} + +/******************************************************************* + 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")); + + if(net_name) + init_unistr2(&sh502str->uni_netname, net_name, strlen(net_name)+1); + if(remark) + init_unistr2(&sh502str->uni_remark, remark, strlen(remark)+1); + if(path) + init_unistr2(&sh502str->uni_path, path, strlen(path)+1); + if(passwd) + init_unistr2(&sh502str->uni_passwd, passwd, strlen(passwd)+1); + 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")); + + if(remark) + init_unistr2(&sh1004->uni_remark, remark, strlen(remark)+1); +} + +/******************************************************************* + 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("dfs_root_flag", ps, depth, &sh1005->dfs_root_flag)) + 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")); + + if(alternate_directory_name) + init_unistr2(&sh1007->uni_AlternateDirectoryName, alternate_directory_name, strlen(alternate_directory_name)+1); +} + +/******************************************************************* + 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, strlen(srvname) +1); + 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, strlen(srvname) +1 ); + init_unistr2(&del->uni_share_name, sharename, strlen(sharename) + 1); +} + +/******************************************************************* + 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, strlen(name)+1); +} + +/******************************************************************* + 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) { + int i; + int 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, strlen(name)+1); + init_unistr2(&ss1->uni_user, user, strlen(user)+1); +} + +/******************************************************************* + 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) { + int i; + int 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 != -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 != -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, strlen(usr_name)+1); + init_unistr2(&ss1->uni_net_name, net_name, strlen(net_name)+1); +} + +/******************************************************************* + 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 != -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 != -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, strlen(path_name)+1); + init_unistr2(&fi3->uni_user_name, user_name, strlen(user_name)+1); +} + +/******************************************************************* + 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 != -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, strlen(server) + 1); + 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) +{ + + 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, strlen(server) + 1); +} + diff --git a/source4/rpc_parse/parse_wks.c b/source4/rpc_parse/parse_wks.c new file mode 100644 index 0000000000..b6de058652 --- /dev/null +++ b/source4/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/source4/rpc_server/.cvsignore b/source4/rpc_server/.cvsignore new file mode 100644 index 0000000000..e69de29bb2 diff --git a/source4/rpc_server/srv_dfs.c b/source4/rpc_server/srv_dfs.c new file mode 100644 index 0000000000..14c1cb4088 --- /dev/null +++ b/source4/rpc_server/srv_dfs.c @@ -0,0 +1,177 @@ +/* + * 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) Anthony Liguori 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 +********************************************************************/ + +#ifdef RPC_DFS_DYNAMIC +int init_module(void) +#else +int rpc_dfs_init(void) +#endif +{ + 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 } + }; + return rpc_pipe_register_commands("netdfs", "netdfs", api_netdfs_cmds, + sizeof(api_netdfs_cmds) / sizeof(struct api_struct)); +} diff --git a/source4/rpc_server/srv_dfs_nt.c b/source4/rpc_server/srv_dfs_nt.c new file mode 100644 index 0000000000..bb9ed87a48 --- /dev/null +++ b/source4/rpc_server/srv_dfs_nt.c @@ -0,0 +1,371 @@ +/* + * 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); + + 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; + + 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)) + return WERR_DFS_CANT_CREATE_JUNCT; + + 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(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)) + return WERR_DFS_NO_SUCH_VOL; + } + 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;ialternate_path); + trim_string(path,"\\",""); + 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, strlen(path)+1); + init_unistr2(&stor->sharename, p+1, strlen(p+1)+1); + 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); + + 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; + 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; + + if(!get_referred_path(path, &jn, NULL, NULL)) + return WERR_DFS_NO_SUCH_VOL; + + 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/source4/rpc_server/srv_lsa.c b/source4/rpc_server/srv_lsa.c new file mode 100644 index 0000000000..0e4039326b --- /dev/null +++ b/source4/rpc_server/srv_lsa.c @@ -0,0 +1,810 @@ +/* + * 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 2002, + * Copyright (C) Anthony Liguori 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_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, &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; +} + + + +/*************************************************************************** + api_lsa_enum_acctrights + ***************************************************************************/ +static BOOL api_lsa_enum_acct_rights(pipes_struct *p) +{ + LSA_Q_ENUM_ACCT_RIGHTS q_u; + LSA_R_ENUM_ACCT_RIGHTS 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_acct_rights("", &q_u, data, 0)) { + DEBUG(0,("api_lsa_enum_acct_rights: failed to unmarshall LSA_Q_ENUM_ACCT_RIGHTS.\n")); + return False; + } + + r_u.status = _lsa_enum_acct_rights(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!lsa_io_r_enum_acct_rights("", &r_u, rdata, 0)) { + DEBUG(0,("api_lsa_enum_acct_rights: Failed to marshall LSA_R_ENUM_ACCT_RIGHTS.\n")); + return False; + } + + return True; +} + + +/*************************************************************************** + api_lsa_enum_acct_with_right + ***************************************************************************/ +static BOOL api_lsa_enum_acct_with_right(pipes_struct *p) +{ + LSA_Q_ENUM_ACCT_WITH_RIGHT q_u; + LSA_R_ENUM_ACCT_WITH_RIGHT 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_acct_with_right("", &q_u, data, 0)) { + DEBUG(0,("api_lsa_enum_acct_with_right: failed to unmarshall LSA_Q_ENUM_ACCT_WITH_RIGHT.\n")); + return False; + } + + r_u.status = _lsa_enum_acct_with_right(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!lsa_io_r_enum_acct_with_right("", &r_u, rdata, 0)) { + DEBUG(0,("api_lsa_enum_acct_with_right: Failed to marshall LSA_R_ENUM_ACCT_WITH_RIGHT.\n")); + return False; + } + + return True; +} + + +/*************************************************************************** + api_lsa_add_acctrights + ***************************************************************************/ +static BOOL api_lsa_add_acct_rights(pipes_struct *p) +{ + LSA_Q_ADD_ACCT_RIGHTS q_u; + LSA_R_ADD_ACCT_RIGHTS 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_add_acct_rights("", &q_u, data, 0)) { + DEBUG(0,("api_lsa_add_acct_rights: failed to unmarshall LSA_Q_ADD_ACCT_RIGHTS.\n")); + return False; + } + + r_u.status = _lsa_add_acct_rights(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!lsa_io_r_add_acct_rights("", &r_u, rdata, 0)) { + DEBUG(0,("api_lsa_add_acct_rights: Failed to marshall LSA_R_ADD_ACCT_RIGHTS.\n")); + return False; + } + + return True; +} + + +/*************************************************************************** + api_lsa_remove_acctrights + ***************************************************************************/ +static BOOL api_lsa_remove_acct_rights(pipes_struct *p) +{ + LSA_Q_REMOVE_ACCT_RIGHTS q_u; + LSA_R_REMOVE_ACCT_RIGHTS 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_remove_acct_rights("", &q_u, data, 0)) { + DEBUG(0,("api_lsa_remove_acct_rights: failed to unmarshall LSA_Q_REMOVE_ACCT_RIGHTS.\n")); + return False; + } + + r_u.status = _lsa_remove_acct_rights(p, &q_u, &r_u); + + /* store the response in the SMB stream */ + if(!lsa_io_r_remove_acct_rights("", &r_u, rdata, 0)) { + DEBUG(0,("api_lsa_remove_acct_rights: Failed to marshall LSA_R_REMOVE_ACCT_RIGHTS.\n")); + return False; + } + + return True; +} + + +/*************************************************************************** + \PIPE\ntlsa commands + ***************************************************************************/ + +#ifdef RPC_LSA_DYNAMIC +int init_module(void) +#else +int rpc_lsa_init(void) +#endif +{ + static const 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_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 }, + { "LSA_QUERYINFO2" , LSA_QUERYINFO2 , api_lsa_query_info2 }, + { "LSA_ENUMACCTRIGHTS" , LSA_ENUMACCTRIGHTS , api_lsa_enum_acct_rights }, + { "LSA_ENUMACCTWITHRIGHT", LSA_ENUMACCTWITHRIGHT, api_lsa_enum_acct_with_right }, + { "LSA_ADDACCTRIGHTS" , LSA_ADDACCTRIGHTS , api_lsa_add_acct_rights }, + { "LSA_REMOVEACCTRIGHTS", LSA_REMOVEACCTRIGHTS, api_lsa_remove_acct_rights}, + }; + + return rpc_pipe_register_commands("lsarpc", "lsass", api_lsa_cmds, + sizeof(api_lsa_cmds) / sizeof(struct api_struct)); +} diff --git a/source4/rpc_server/srv_lsa_hnd.c b/source4/rpc_server/srv_lsa_hnd.c new file mode 100644 index 0000000000..814fa60aab --- /dev/null +++ b/source4/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 = %u for pipe %s\n", + 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/source4/rpc_server/srv_lsa_nt.c b/source4/rpc_server/srv_lsa_nt.c new file mode 100644 index 0000000000..3af3e75e6b --- /dev/null +++ b/source4/rpc_server/srv_lsa_nt.c @@ -0,0 +1,1399 @@ +/* + * 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 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) +{ + int domlen = (dom_name != NULL) ? strlen(dom_name) : 0; + + /* + * 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. + */ + + d_q->uni_dom_str_len = domlen ? ((domlen + 1) * 2) : 0; + d_q->uni_dom_max_len = domlen * 2; + d_q->buffer_dom_name = domlen != 0 ? 1 : 0; /* domain buffer pointer */ + d_q->buffer_dom_sid = dom_sid != NULL ? 1 : 0; /* domain sid pointer */ + + /* this string is supposed to be character short */ + init_unistr2(&d_q->uni_domain_name, dom_name, domlen); + d_q->uni_domain_name.uni_max_len++; + + 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; + int len; + + 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; + + len = (dom_name != NULL) ? strlen(dom_name) : 0; + if(dom_name != NULL && len == 0) + len = 1; + + init_uni_hdr(&ref->hdr_ref_dom[num].hdr_dom_name, len); + ref->hdr_ref_dom[num].ptr_dom_sid = dom_sid != NULL ? 1 : 0; + + init_unistr2(&ref->ref_dom[num].uni_dom_name, dom_name, len); + 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); + + 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 = 0xffffffff; + 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; + + if (mapped_count == 0) + r_l->status = NT_STATUS_NONE_MAPPED; + else + r_l->status = NT_STATUS_OK; +} + +/*************************************************************************** + 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 */ + + memset(dom_name, '\0', sizeof(dom_name)); + memset(name, '\0', sizeof(name)); + + 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; + } 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; + + if (mapped_count == 0) + r_l->status = NT_STATUS_NONE_MAPPED; + else + r_l->status = NT_STATUS_OK; +} + +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, &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, + GUID *dom_guid, DOM_SID *dom_sid) +{ + if (nb_name && *nb_name) { + init_uni_hdr(&r_l->hdr_nb_dom_name, strlen(nb_name)); + init_unistr2(&r_l->uni_nb_dom_name, nb_name, + strlen(nb_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_uni_hdr(&r_l->hdr_dns_dom_name, strlen(dns_name)); + init_unistr2(&r_l->uni_dns_dom_name, dns_name, + strlen(dns_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_uni_hdr(&r_l->hdr_forest_name, strlen(forest_name)); + init_unistr2(&r_l->uni_forest_name, forest_name, + strlen(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(GUID)); + } + + 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)) + return status; + + /* 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)) + return status; + + /* 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, &enum_context, max_num_domains, &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 = lp_workgroup(); + 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. */ + switch (lp_server_role()) { + case ROLE_DOMAIN_PDC: + case ROLE_DOMAIN_BDC: + name = lp_workgroup(); + sid = get_global_sam_sid(); + break; + case ROLE_DOMAIN_MEMBER: + name = lp_netbios_name(); + sid = get_global_sam_sid(); + break; + case ROLE_STANDALONE: + name = lp_netbios_name(); + sid = get_global_sam_sid(); + break; + default: + return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; + } + 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; + + 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); + 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); + 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) + 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 = 0; i < PRIV_ALL_INDEX; i++, entry++) { + if( ihdr_name, 0); + init_unistr2(&entry->name, NULL, 0 ); + entry->luid_low = 0; + entry->luid_high = 0; + } else { + init_uni_hdr(&entry->hdr_name, strlen(privs[i+1].priv)); + init_unistr2(&entry->name, privs[i+1].priv, strlen(privs[i+1].priv) ); + entry->luid_low = privs[i+1].se_priv; + entry->luid_high = 0; + } + } + + enum_context = PRIV_ALL_INDEX; + init_lsa_r_enum_privs(r_u, enum_context, PRIV_ALL_INDEX, 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_PRIV_ALL && strcmp(name_asc, privs[i].priv)) + i++; + + if (privs[i].se_priv!=SE_PRIV_ALL) { + DEBUG(10,(": %s\n", privs[i].description)); + init_uni_hdr(&r_u->hdr_desc, strlen(privs[i].description)); + init_unistr2(&r_u->desc, privs[i].description, strlen(privs[i].description) ); + + 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; + GROUP_MAP *map=NULL; + int num_entries=0; + LSA_SID_ENUM *sids=&r_u->sids; + int i=0,j=0; + + 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; + + /* get the list of mapped groups (domain, local, builtin) */ + if(!pdb_enum_group_mapping(SID_NAME_UNKNOWN, &map, &num_entries, ENUM_ONLY_MAPPED, MAPPING_WITHOUT_PRIV)) + return NT_STATUS_OK; + + 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(map); + return NT_STATUS_NO_MEMORY; + } + + for (i=q_u->enum_context, j=0; ivuid); + + if (vuser == NULL) + return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; + + fstrcpy(username, vuser->user.smb_name); + fstrcpy(domname, vuser->user.domain); + + ulen = strlen(username) + 1; + dlen = strlen(domname) + 1; + + init_uni_hdr(&r_u->hdr_user_name, ulen); + r_u->ptr_user_name = 1; + init_unistr2(&r_u->uni2_user_name, username, ulen); + + r_u->unk1 = 1; + + init_uni_hdr(&r_u->hdr_dom_name, dlen); + r_u->ptr_dom_name = 1; + init_unistr2(&r_u->uni2_dom_name, domname, dlen); + + r_u->status = NT_STATUS_OK; + + return r_u->status; +} + +/*************************************************************************** + + ***************************************************************************/ + +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, LSA_Q_ENUMPRIVSACCOUNT *q_u, LSA_R_ENUMPRIVSACCOUNT *r_u) +{ + struct lsa_info *info=NULL; + GROUP_MAP map; + int i=0; + + LUID_ATTR *set=NULL; + + 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, MAPPING_WITH_PRIV)) + return NT_STATUS_NO_SUCH_GROUP; + + DEBUG(10,("_lsa_enum_privsaccount: %d privileges\n", map.priv_set.count)); + if (map.priv_set.count!=0) { + + set=(LUID_ATTR *)talloc(p->mem_ctx, map.priv_set.count*sizeof(LUID_ATTR)); + if (set == NULL) { + free_privilege(&map.priv_set); + return NT_STATUS_NO_MEMORY; + } + + for (i=0; istatus; +} + +/*************************************************************************** + + ***************************************************************************/ + +NTSTATUS _lsa_getsystemaccount(pipes_struct *p, LSA_Q_GETSYSTEMACCOUNT *q_u, LSA_R_GETSYSTEMACCOUNT *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, MAPPING_WITHOUT_PRIV)) + return NT_STATUS_NO_SUCH_GROUP; + + /* + 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=map.systemaccount; + + 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, MAPPING_WITH_PRIV)) + return NT_STATUS_NO_SUCH_GROUP; + + map.systemaccount=q_u->access; + + if(!pdb_update_group_mapping_entry(&map)) + return NT_STATUS_NO_SUCH_GROUP; + + free_privilege(&map.priv_set); + + 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) +{ + struct lsa_info *info=NULL; + GROUP_MAP map; + int i=0; + + LUID_ATTR *luid_attr=NULL; + PRIVILEGE_SET *set=NULL; + + 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, MAPPING_WITH_PRIV)) + return NT_STATUS_NO_SUCH_GROUP; + + set=&q_u->set; + + for (i=0; icount; i++) { + luid_attr=&set->set[i]; + + /* check if the privilege is already there */ + if (check_priv_in_privilege(&map.priv_set, *luid_attr)){ + free_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; + + free_privilege(&map.priv_set); + + 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) +{ + struct lsa_info *info=NULL; + GROUP_MAP map; + int i=0; + + LUID_ATTR *luid_attr=NULL; + PRIVILEGE_SET *set=NULL; + + 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, MAPPING_WITH_PRIV)) + 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; icount; 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)){ + free_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; + + free_privilege(&map.priv_set); + + 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; + GUID guid; + + 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 = lp_workgroup(); + /* ugly temp hack for these next two */ + dns_name = lp_realm(); + forest_name = lp_realm(); + 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; +} + + +/*************************************************************************** + For a given SID, enumerate all the privilege this account has. + ***************************************************************************/ +NTSTATUS _lsa_enum_acct_rights(pipes_struct *p, LSA_Q_ENUM_ACCT_RIGHTS *q_u, LSA_R_ENUM_ACCT_RIGHTS *r_u) +{ + struct lsa_info *info=NULL; + char **rights = NULL; + int num_rights = 0; + int i; + + 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; + + r_u->status = privilege_enum_account_rights(&q_u->sid.sid, &num_rights, &rights); + + init_r_enum_acct_rights(r_u, num_rights, (const char **)rights); + + for (i=0;istatus; +} + +/*************************************************************************** +return a list of SIDs for a particular privilege + ***************************************************************************/ +NTSTATUS _lsa_enum_acct_with_right(pipes_struct *p, + LSA_Q_ENUM_ACCT_WITH_RIGHT *q_u, + LSA_R_ENUM_ACCT_WITH_RIGHT *r_u) +{ + struct lsa_info *info=NULL; + char *right; + DOM_SID *sids = NULL; + uint32 count = 0; + + 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; + + right = unistr2_tdup(p->mem_ctx, &q_u->right); + + DEBUG(5,("lsa_enum_acct_with_right on right %s\n", right)); + + r_u->status = privilege_enum_account_with_right(right, &count, &sids); + + init_r_enum_acct_with_right(r_u, count, sids); + + safe_free(sids); + + return r_u->status; +} + +/*************************************************************************** + add privileges to a acct by SID + ***************************************************************************/ +NTSTATUS _lsa_add_acct_rights(pipes_struct *p, LSA_Q_ADD_ACCT_RIGHTS *q_u, LSA_R_ADD_ACCT_RIGHTS *r_u) +{ + struct lsa_info *info=NULL; + int i; + + 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; + + DEBUG(5,("_lsa_add_acct_rights to %s (%d rights)\n", + sid_string_static(&q_u->sid.sid), q_u->rights.count)); + + for (i=0;irights.count;i++) { + DEBUG(5,("\t%s\n", unistr2_static(&q_u->rights.strings[i].string))); + } + + + for (i=0;irights.count;i++) { + r_u->status = privilege_add_account_right(unistr2_static(&q_u->rights.strings[i].string), + &q_u->sid.sid); + if (!NT_STATUS_IS_OK(r_u->status)) { + DEBUG(2,("Failed to add right '%s'\n", + unistr2_static(&q_u->rights.strings[i].string))); + break; + } + } + + init_r_add_acct_rights(r_u); + + return r_u->status; +} + + +/*************************************************************************** + remove privileges from a acct by SID + ***************************************************************************/ +NTSTATUS _lsa_remove_acct_rights(pipes_struct *p, LSA_Q_REMOVE_ACCT_RIGHTS *q_u, LSA_R_REMOVE_ACCT_RIGHTS *r_u) +{ + struct lsa_info *info=NULL; + int i; + + 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; + + + DEBUG(5,("_lsa_remove_acct_rights from %s all=%d (%d rights)\n", + sid_string_static(&q_u->sid.sid), + q_u->removeall, + q_u->rights.count)); + + for (i=0;irights.count;i++) { + DEBUG(5,("\t%s\n", unistr2_static(&q_u->rights.strings[i].string))); + } + + for (i=0;irights.count;i++) { + r_u->status = privilege_remove_account_right(unistr2_static(&q_u->rights.strings[i].string), + &q_u->sid.sid); + if (!NT_STATUS_IS_OK(r_u->status)) { + DEBUG(2,("Failed to remove right '%s'\n", + unistr2_static(&q_u->rights.strings[i].string))); + break; + } + } + + init_r_remove_acct_rights(r_u); + + return r_u->status; +} diff --git a/source4/rpc_server/srv_netlog.c b/source4/rpc_server/srv_netlog.c new file mode 100644 index 0000000000..c9e4fc1b1f --- /dev/null +++ b/source4/rpc_server/srv_netlog.c @@ -0,0 +1,345 @@ +/* + * 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) Anthony Liguori 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; +} + +/******************************************************************* + array of \PIPE\NETLOGON operations + ********************************************************************/ + +#ifdef RPC_NETLOG_DYNAMIC +int init_module(void) +#else +int rpc_net_init(void) +#endif +{ + 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 } + }; + + return rpc_pipe_register_commands("NETLOGON", "lsass", api_net_cmds, + sizeof(api_net_cmds) / sizeof(struct api_struct)); +} diff --git a/source4/rpc_server/srv_netlog_nt.c b/source4/rpc_server/srv_netlog_nt.c new file mode 100644 index 0000000000..daf3e2ae07 --- /dev/null +++ b/source4/rpc_server/srv_netlog_nt.c @@ -0,0 +1,743 @@ +/* + * 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. + * Copyirht (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; + + 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; + + /* set up the LSA AUTH 2 response */ + init_net_r_auth_2(r_u, &srv_cred, &srv_flgs, status); + + 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; + + 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; + + /* 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 */ + + /* check username exists */ + + 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); + + /* + * Convert to a UNIX username. + */ + + DEBUG(5,("Attempting validation level %d for unmapped username %s.\n", q_u->sam_id.ctr->switch_value, nt_username)); + + switch (ctr->switch_value) { + case NET_LOGON_TYPE: + { + struct auth_context *auth_context = NULL; + if (!NT_STATUS_IS_OK(status = make_auth_context_fixed(&auth_context, ctr->auth.id2.lm_chal))) { + return status; + } + + /* Standard challenge/response authenticaion */ + if (!make_user_info_netlogon_network(&user_info, + nt_username, nt_domain, + nt_workstation, + 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; + } else { + status = auth_context->check_ntlm_password(auth_context, user_info, &server_info); + } + (auth_context->free)(&auth_context); + + 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 */ + { + struct auth_context *auth_context = NULL; + 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; + } else { + status = auth_context->check_ntlm_password(auth_context, user_info, &server_info); + } + + (auth_context->free)(&auth_context); + + break; + } + default: + DEBUG(2,("SAM Logon: unsupported switch value\n")); + return NT_STATUS_INVALID_INFO_CLASS; + } /* end switch */ + + 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 user_sess_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, lp_netbios_name()); + + 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); + memcpy(user_sess_key, server_info->session_key, sizeof(user_sess_key)); + SamOEMhash(user_sess_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 (?) */ + user_sess_key, + 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(user_sess_key); + } + free_server_info(&server_info); + return status; +} + + diff --git a/source4/rpc_server/srv_pipe.c b/source4/rpc_server/srv_pipe.c new file mode 100644 index 0000000000..f6deac68f8 --- /dev/null +++ b/source4/rpc_server/srv_pipe.c @@ -0,0 +1,1386 @@ +/* + * 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) Anthony Liguori 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 + +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); + + /* + * 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 { + 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->hdr.auth_len > 0) { + 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, NTLMSSP_AUTH_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); + } + } + + /* + * 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 . + */ + + 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. + */ + + { + uchar p24[24]; + NTLMSSPOWFencrypt(server_info->first_8_lm_hash, 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; + } +/* NTLMSSPhash(p->ntlmssp_hash, p24); */ + p->ntlmssp_seq_num = 0; + + } + + fstrcpy(p->user_name, user_name); + fstrcpy(p->pipe_user_name, pdb_get_username(server_info->sam_account)); + fstrcpy(p->domain, domain); + fstrcpy(p->wks, wks); + + /* + * Store the UNIX credential data (uid/gid pair) in the pipe structure. + */ + + if (!IS_SAM_UNIX_USER(server_info->sam_account)) { + DEBUG(0,("Attempted authenticated pipe with invalid user. No uid/gid in SAM_ACCOUNT\n")); + free_server_info(&server_info); + return False; + } + + memcpy(p->session_key, server_info->session_key, sizeof(p->session_key)); + + p->pipe_user.uid = pdb_get_uid(server_info->sam_account); + p->pipe_user.gid = pdb_get_gid(server_info->sam_account); + + 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 api_cmd +{ + const char *name; + int (*init)(void); +}; + +static struct api_cmd api_fd_commands[] = +{ +#ifndef RPC_LSA_DYNAMIC + { "lsarpc", rpc_lsa_init }, +#endif +#ifndef RPC_SAMR_DYNAMIC + { "samr", rpc_samr_init }, +#endif +#ifndef RPC_SVC_DYNAMIC + { "srvsvc", rpc_srv_init }, +#endif +#ifndef RPC_WKS_DYNAMIC + { "wkssvc", rpc_wks_init }, +#endif +#ifndef RPC_NETLOG_DYNAMIC + { "NETLOGON", rpc_net_init }, +#endif +#ifndef RPC_REG_DYNAMIC + { "winreg", rpc_reg_init }, +#endif +#ifndef RPC_SPOOLSS_DYNAMIC + { "spoolss", rpc_spoolss_init }, +#endif +#ifndef RPC_DFS_DYNAMIC + { "netdfs", rpc_dfs_init }, +#endif + { NULL, NULL } +}; + +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 != NTLMSSP_AUTH_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(char* pipe_name, RPC_IFACE* abstract, + RPC_IFACE* transfer) +{ + extern struct pipe_id_info pipe_names[]; + int i=0; + fstring pname; + fstrcpy(pname,"\\PIPE\\"); + fstrcat(pname,pipe_name); + + DEBUG(3,("check_bind_req for %s\n", pname)); + +#ifndef SUPPORT_NEW_LSARPC_UUID + + /* check for the first pipe matching the name */ + + for ( i=0; pipe_names[i].client_pipe; i++ ) { + if ( strequal(pipe_names[i].client_pipe, pname) ) + break; + } +#else + /* 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(RPC_UUID)) == 0) + && (transfer->version == pipe_names[i].trans_syntax.version) + && (memcmp(&transfer->uuid, &pipe_names[i].trans_syntax.uuid, sizeof(RPC_UUID)) == 0) ) + { + break; + } + } +#endif + + if(pipe_names[i].client_pipe == NULL) + return False; + +#ifndef SUPPORT_NEW_LSARPC_UUID + /* check the abstract interface */ + if ( (abstract->version != pipe_names[i].abstr_syntax.version) + || (memcmp(&abstract->uuid, &pipe_names[i].abstr_syntax.uuid, sizeof(RPC_UUID)) != 0) ) + { + return False; + } + + /* check the transfer interface */ + if ( (transfer->version != pipe_names[i].trans_syntax.version) + || (memcmp(&transfer->uuid, &pipe_names[i].trans_syntax.uuid, sizeof(RPC_UUID)) != 0) ) + { + return False; + } +#endif + return True; +} + +/******************************************************************* + Register commands to an RPC pipe +*******************************************************************/ +int rpc_pipe_register_commands(const char *clnt, const char *srv, const struct api_struct *cmds, int size) +{ + struct rpc_table *rpc_entry; + + + /* 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 0; + } 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 size; +} + +/******************************************************************* + Register commands to an RPC pipe +*******************************************************************/ +int rpc_load_module(const char *module) +{ + pstring full_path; + int status; + + pstrcpy(full_path, lib_path("rpc")); + pstrcat(full_path, "/librpc_"); + pstrcat(full_path, module); + pstrcat(full_path, "."); + pstrcat(full_path, shlib_ext()); + + if (!(status = smb_load_module(full_path))) { + DEBUG(0, ("Could not load requested pipe %s as %s\n", + module, full_path)); + } + + return status; +} + +/******************************************************************* + 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; + + 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) { + for (i = 0; api_fd_commands[i].name; i++) { + if (strequal(api_fd_commands[i].name, p->name)) { + api_fd_commands[i].init(); + break; + } + } + + if (!api_fd_commands[i].name && !rpc_load_module(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; + } + } + } + + /* 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; + } + + /* + * We only support NTLMSSP_AUTH_TYPE requests. + */ + + if(auth_info.auth_type != NTLMSSP_AUTH_TYPE) { + DEBUG(0,("api_pipe_bind_req: unknown auth type %x requested.\n", + auth_info.auth_type )); + return False; + } + + 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; + } + + 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->name, &hdr_rb.abstract, &hdr_rb.transfer)) { + 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, NTLMSSP_AUTH_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; + } + + /* + * 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 && !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; + NTLMSSPcalc_p(p, (uchar*)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; +} + +/**************************************************************************** + 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, ¤t_user, sizeof(struct current_user)); + } + + return user; +} + +/**************************************************************************** + 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) +{ + int i = 0; + BOOL ret = False; + + 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)); + + for (i = 0; i < rpc_lookup_size; i++) { + if (strequal(rpc_lookup[i].pipe.clnt, p->name)) { + DEBUG(3,("Doing \\PIPE\\%s\n", + rpc_lookup[i].pipe.clnt)); + set_current_rpc_talloc(p->mem_ctx); + ret = api_rpcTNP(p, rpc_lookup[i].pipe.clnt, + rpc_lookup[i].cmds, + rpc_lookup[i].n_cmds); + set_current_rpc_talloc(NULL); + break; + } + } + + + if (i == rpc_lookup_size) { + for (i = 0; api_fd_commands[i].name; i++) { + if (strequal(api_fd_commands[i].name, p->name)) { + api_fd_commands[i].init(); + break; + } + } + + if (!api_fd_commands[i].name) { + rpc_load_module(p->name); + } + + for (i = 0; i < rpc_lookup_size; i++) { + if (strequal(rpc_lookup[i].pipe.clnt, p->name)) { + DEBUG(3,("Doing \\PIPE\\%s\n", + rpc_lookup[i].pipe.clnt)); + set_current_rpc_talloc(p->mem_ctx); + ret = api_rpcTNP(p, rpc_lookup[i].pipe.clnt, + rpc_lookup[i].cmds, + rpc_lookup[i].n_cmds); + set_current_rpc_talloc(NULL); + break; + } + } + } + + 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; +} diff --git a/source4/rpc_server/srv_pipe_hnd.c b/source4/rpc_server/srv_pipe_hnd.c new file mode 100644 index 0000000000..602a7ed0ab --- /dev/null +++ b/source4/rpc_server/srv_pipe_hnd.c @@ -0,0 +1,1156 @@ +/* + * 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, + struct tcon_context *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, + struct tcon_context *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, + struct tcon_context *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) { + memcpy(p->session_key, vuser->session_key, sizeof(p->session_key)); + p->pipe_user.nt_user_token = dup_nt_token(vuser->nt_user_token); + } + + /* + * 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 %u\n", 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; + } + + /* + * 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); + + 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 ? + */ + + if(p->in_data.pdu_received_len == p->in_data.pdu_needed_len) + return process_complete_pdu(p); + + 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 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/source4/rpc_server/srv_reg.c b/source4/rpc_server/srv_reg.c new file mode 100644 index 0000000000..8fc1d42b2f --- /dev/null +++ b/source4/rpc_server/srv_reg.c @@ -0,0 +1,400 @@ +/* + * 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) Anthony Liguori 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 + ********************************************************************/ + +#ifdef RPC_REG_DYNAMIC +int init_module(void) +#else +int rpc_reg_init(void) +#endif +{ + 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 } + }; + return rpc_pipe_register_commands("winreg", "winreg", api_reg_cmds, + sizeof(api_reg_cmds) / sizeof(struct api_struct)); +} diff --git a/source4/rpc_server/srv_reg_nt.c b/source4/rpc_server/srv_reg_nt.c new file mode 100644 index 0000000000..5632544909 --- /dev/null +++ b/source4/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 **)®key)) { + 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]\\\... + *******************************************************************/ + +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; ivaluename)+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( ®vals ); + + regval_ctr_init( ®vals ); + + /* 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(®vals, REGSTR_PRODUCTTYPE, REG_SZ, + value, value_length); + + val = dup_registry_value( regval_ctr_specific_value( ®vals, 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( ®vals ); + 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 */ + snprintf(timeout, sizeof(timeout), "%d", q_u->timeout); + /* reboot */ + snprintf(r, sizeof(r), (q_u->reboot) ? SHUTDOWN_R_STRING : ""); + /* force */ + snprintf(f, sizeof(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/source4/rpc_server/srv_samr.c b/source4/rpc_server/srv_samr.c new file mode 100644 index 0000000000..b75195ceef --- /dev/null +++ b/source4/rpc_server/srv_samr.c @@ -0,0 +1,1510 @@ +/* + * 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) Anthony Liguori 2002-2003, + * Copyright (C) Jim McDonough 2002. + * + * 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); + + /* unknown 38 command */ + 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); + + /* grab the samr unknown 22 */ + 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 = _api_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); + + /* grab the samr unknown 24 */ + 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); + + /* grab the samr unknown 32 */ + 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); + + /* grab the samr unknown 8 command */ + 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=_api_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=_api_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_unknown_2d + ********************************************************************/ + +static BOOL api_samr_unknown_2d(pipes_struct *p) +{ + SAMR_Q_UNKNOWN_2D q_u; + SAMR_R_UNKNOWN_2D 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_unknown_2d("", &q_u, data, 0)) { + DEBUG(0,("api_samr_unknown_2d: unable to unmarshall SAMR_Q_UNKNOWN_2D.\n")); + return False; + } + + r_u.status = _samr_unknown_2d(p, &q_u, &r_u); + + if (!samr_io_r_unknown_2d("", &r_u, rdata, 0)) { + DEBUG(0,("api_samr_unknown_2d: unable to marshall SAMR_R_UNKNOWN_2D.\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); + + /* grab the samr unknown 8 command */ + 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 + ********************************************************************/ + +#ifdef RPC_SAMR_DYNAMIC +int init_module(void) +#else +int rpc_samr_init(void) +#endif +{ + 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_UNKNOWN_2D" , SAMR_UNKNOWN_2D , api_samr_unknown_2d }, + {"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 } + }; + return rpc_pipe_register_commands("samr", "lsass", api_samr_cmds, + sizeof(api_samr_cmds) / sizeof(struct api_struct)); +} diff --git a/source4/rpc_server/srv_samr_nt.c b/source4/rpc_server/srv_samr_nt.c new file mode 100644 index 0000000000..fd1fb92982 --- /dev/null +++ b/source4/rpc_server/srv_samr_nt.c @@ -0,0 +1,4432 @@ +/* + * 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) Anthony Liguori 2002, + * Copyright (C) Jim McDonough 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 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[]; + + +typedef struct _disp_info { + BOOL user_dbloaded; + uint32 num_user_account; + DISP_USER_INFO *disp_user_info; + BOOL group_dbloaded; + uint32 num_group_account; + DISP_GROUP_INFO *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, NT_USER_TOKEN *nt_user_token, uint32 des_access, + uint32 *acc_granted, const char *debug) +{ + NTSTATUS status = NT_STATUS_ACCESS_DENIED; + + if (!se_access_check(psd, nt_user_token, des_access, acc_granted, &status)) { + 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")); + status = NT_STATUS_OK; + } + else { + 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(uint32 acc_granted, uint32 acc_required, const char *debug) +{ + 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; + } + 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; idisp_info.num_user_account; i++) { + /* Not really a free, actually a 'clear' */ + pdb_free_sam(&info->disp_info.disp_user_info[i].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; + DISP_USER_INFO *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=(DISP_USER_INFO *)talloc_realloc(mem_ctx, info->disp_info.disp_user_info, + (info->disp_info.num_user_account+MAX_SAM_ENTRIES)*sizeof(DISP_USER_INFO)); + + if (pwd_array==NULL) + return NT_STATUS_NO_MEMORY; + + info->disp_info.disp_user_info=pwd_array; + } + + /* link the SAM_ACCOUNT to the array */ + info->disp_info.disp_user_info[info->disp_info.num_user_account].sam=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; + DISP_GROUP_INFO *grp_array = NULL; + uint32 group_entries = 0; + uint32 i; + TALLOC_CTX *mem_ctx = info->mem_ctx; + + 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; + } + + if (!pdb_enum_group_mapping(SID_NAME_DOM_GRP, &map, (int *)&group_entries, ENUM_ONLY_MAPPED, MAPPING_WITHOUT_PRIV)) { + 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=(DISP_GROUP_INFO *)talloc(mem_ctx, info->disp_info.num_group_account*sizeof(DISP_GROUP_INFO)); + + 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; iname, map[i].nt_name); + fstrcpy(grp_array[i].grp->comment, map[i].comment); + sid_split_rid(&map[i].sid, &grp_array[i].grp->rid); + grp_array[i].grp->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; + + 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(info->acc_granted, SA_RIGHT_SAM_OPEN_DOMAIN,"_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->pipe_user.nt_user_token, + des_access, &acc_granted, "_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, 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, 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, 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, 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, DISP_USER_INFO *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].sam; + temp_name = pdb_get_username(pwd); + init_unistr2(&uni_temp_name, temp_name, strlen(temp_name)+1); + 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.uni_str_len, 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(info->acc_granted, + SA_RIGHT_DOMAIN_ENUM_ACCOUNTS, + "_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. + */ + int len = strlen(grp[i].name)+1; + + init_sam_entry(&sam[i], len, grp[i].rid); + init_unistr2(&uni_name[i], grp[i].name, len); + } + + *sam_pp = sam; + *uni_name_pp = uni_name; +} + +/******************************************************************* + Get the group entries - similar to get_sampwd_entries(). + ********************************************************************/ + +static NTSTATUS get_group_alias_entries(TALLOC_CTX *ctx, DOMAIN_GRP **d_grp, DOM_SID *sid, uint32 start_idx, + uint32 *p_num_entries, uint32 max_entries) +{ + fstring sid_str; + uint32 num_entries = 0; + int i; + GROUP_MAP smap; + GROUP_MAP *map = NULL; + + sid_to_string(sid_str, sid); + DEBUG(5, ("get_group_alias_entries: enumerating aliases on SID: %s\n", sid_str)); + + *p_num_entries = 0; + + /* well-known aliases */ + if (sid_equal(sid, &global_sid_Builtin) && !lp_hide_local_users()) { + + pdb_enum_group_mapping(SID_NAME_WKN_GRP, &map, (int *)&num_entries, ENUM_ONLY_MAPPED, MAPPING_WITHOUT_PRIV); + + if (num_entries != 0) { + *d_grp=(DOMAIN_GRP *)talloc_zero(ctx, num_entries*sizeof(DOMAIN_GRP)); + if (*d_grp==NULL) + return NT_STATUS_NO_MEMORY; + + for(i=0; inext) { + uint32 trid; + + if(!pdb_getgrgid(&smap, grp->gr_gid, MAPPING_WITHOUT_PRIV)) + continue; + + if (smap.sid_name_use!=SID_NAME_ALIAS) { + continue; + } + + sid_split_rid(&smap.sid, &trid); + + if (!sid_equal(sid, &smap.sid)) + continue; + + /* Don't return winbind groups as they are not local! */ + if (winbind_groups_exist && (grp->gr_gid >= winbind_gid_low)&&(grp->gr_gid <= winbind_gid_high)) { + DEBUG(10,("get_group_alias_entries: not returing %s, not local.\n", smap.nt_name )); + continue; + } + + /* Don't return user private groups... */ + + if ((pw = Get_Pwnam(smap.nt_name)) != 0) { + DEBUG(10,("get_group_alias_entries: not returing %s, clashes with user.\n", smap.nt_name )); + continue; + } + + for( i = 0; i < num_entries; i++) + if ( (*d_grp)[i].rid == trid ) + break; + + if ( i < num_entries ) { + continue; /* rid was there, dup! */ + } + + /* JRA - added this for large group db enumeration... */ + + if (start_idx > 0) { + /* skip the requested number of entries. + not very efficient, but hey... + */ + start_idx--; + continue; + } + + *d_grp=talloc_realloc(ctx,*d_grp, (num_entries+1)*sizeof(DOMAIN_GRP)); + if (*d_grp==NULL) { + grent_free(glist); + return NT_STATUS_NO_MEMORY; + } + + fstrcpy((*d_grp)[num_entries].name, smap.nt_name); + (*d_grp)[num_entries].rid = trid; + num_entries++; + DEBUG(10,("get_group_alias_entries: added entry %d, rid:%d\n", num_entries, trid)); + } + + grent_free(glist); + } + + *p_num_entries = num_entries; + + DEBUG(10,("get_group_alias_entries: returning %d entries\n", *p_num_entries)); + + if (num_entries >= max_entries) + return STATUS_MORE_ENTRIES; + return NT_STATUS_OK; +} + +/******************************************************************* + 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; + + pdb_enum_group_mapping(SID_NAME_DOM_GRP, &map, (int *)&group_entries, ENUM_ONLY_MAPPED, MAPPING_WITHOUT_PRIV); + + 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; istatus = 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(acc_granted, SA_RIGHT_DOMAIN_ENUM_ACCOUNTS, "_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(acc_granted, SA_RIGHT_DOMAIN_ENUM_ACCOUNTS, "_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_group_alias_entries(p->mem_ctx, &grp, &sid, q_u->start_idx, + &num_entries, MAX_SAM_ENTRIES); + if (NT_STATUS_IS_ERR(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; + GROUP_MAP map; + uint32 acc_granted; + + 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(acc_granted, SA_RIGHT_ALIAS_LOOKUP_INFO, "_samr_query_aliasinfo"))) { + return r_u->status; + } + + if (!sid_check_is_in_our_domain(&sid) && + !sid_check_is_in_builtin(&sid)) + return NT_STATUS_OBJECT_TYPE_MISMATCH; + + if (!pdb_getgrsid(&map, sid, MAPPING_WITHOUT_PRIV)) + 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, map.nt_name, 1, map.comment); + break; + case 3: + r_u->ptr = 1; + r_u->ctr.switch_value1 = 3; + init_samr_alias_info3(&r_u->ctr.alias.info3, map.comment); + 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(acc_granted, 0, "_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))); + + become_root(); /* local_lookup_name can require root privs */ + + for (i = 0; i < num_rids; i++) { + fstring name; + DOM_SID sid; + + r_u->status = NT_STATUS_NONE_MAPPED; + + rid [i] = 0xffffffff; + type[i] = SID_NAME_UNKNOWN; + + 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(local_lookup_name(name, &sid, &local_type)) { + sid_split_rid(&sid, &local_rid); + + if (sid_equal(&sid, &pol_sid)) { + rid[i]=local_rid; + type[i]=local_type; + r_u->status = NT_STATUS_OK; + } + } + } + + unbecome_root(); + + 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++) { + int len = names[i] != NULL ? strlen(names[i]) : 0; + DEBUG(10, ("names[%d]:%s\n", i, names[i])); + init_uni_hdr(&hdr_name[i], len); + init_unistr2(&uni_name[i], names[i], len); + } + + *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; +} + +/******************************************************************* + _api_samr_open_user. Safe - gives out no passwd info. + ********************************************************************/ + +NTSTATUS _api_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; + + 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(acc_granted, SA_RIGHT_DOMAIN_OPEN_ACCOUNT, "_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->pipe_user.nt_user_token, + des_access, &acc_granted, "_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(acc_granted, SA_RIGHT_USER_GET_GROUPS, "_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(), lp_netbios_name(), (uint32) time(NULL), + num_users, num_groups, num_aliases); + break; + case 0x03: + account_policy_get(AP_TIME_TO_LOGOUT, (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, lp_netbios_name()); + 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; + + account_policy_get(AP_RESET_COUNT_TIME, &account_policy_temp); + u_reset_time = account_policy_temp; + + 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; +} + +/******************************************************************* + _api_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 _api_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 des_access; + + /* 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(acc_granted, SA_RIGHT_DOMAIN_CREATE_USER, "_samr_create_user"))) { + return nt_status; + } + + /* 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(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 %d for $ termination\n",account, strlen(account)-1)); +#if 0 + if ((acb_info & ACB_WSTRUST) && (account[strlen(account)-1] == '$')) { + pstrcpy(add_script, lp_addmachine_script()); + } else if ((!(acb_info & ACB_WSTRUST)) && (account[strlen(account)-1] != '$')) { + pstrcpy(add_script, lp_adduser_script()); + } else { + DEBUG(0, ("_api_samr_create_user: mismatch between trust flags and $ termination\n")); + pdb_free_sam(&sam_pass); + return NT_STATUS_UNSUCCESSFUL; + } +#endif + + /* + * 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] == '$') + pstrcpy(add_script, lp_addmachine_script()); + else + pstrcpy(add_script, lp_adduser_script()); + + if (*add_script) { + int add_ret; + all_string_sub(add_script, "%u", account, sizeof(account)); + add_ret = smbrun(add_script,NULL); + DEBUG(3,("_api_samr_create_user: Running the command `%s' gave %d\n", add_script, add_ret)); + } + + pw = getpwnam_alloc(account); + + if (pw) { + if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam_pw(&sam_pass, pw))) { + passwd_free(&pw); + return nt_status; + } + passwd_free(&pw); /* done with this now */ + } else { + DEBUG(3,("attempting to create non-unix account %s\n", account)); + + if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam(&sam_pass))) { + return nt_status; + } + + if (!pdb_set_username(sam_pass, account, PDB_CHANGED)) { + pdb_free_sam(&sam_pass); + return NT_STATUS_NO_MEMORY; + } + } + + 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. Check permissions?\n", + account)); + return NT_STATUS_ACCESS_DENIED; + } + + pdb_reset_sam(sam_pass); + + if (!pdb_getsampwnam(sam_pass, account)) { + pdb_free_sam(&sam_pass); + DEBUG(0, ("could not find user/computer %s just added to passdb?!?\n", + account)); + return NT_STATUS_ACCESS_DENIED; + } + + /* 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->pipe_user.nt_user_token, + des_access, &acc_granted, "_samr_create_user"))) { + return nt_status; + } + + /* associate the user's SID with the new handle. */ + if ((info = get_samr_info_by_sid(&sid)) == NULL) { + pdb_free_sam(&sam_pass); + return NT_STATUS_NO_MEMORY; + } + + 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); + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + r_u->user_rid=pdb_get_user_rid(sam_pass); + + r_u->access_granted = acc_granted; + + pdb_free_sam(&sam_pass); + + return NT_STATUS_OK; +} + +/******************************************************************* + 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; + + /* 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; + + 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->pipe_user.nt_user_token, + des_access, &acc_granted, "_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->pipe_user.nt_user_token, + des_access, &acc_granted, "_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(info->acc_granted, SA_RIGHT_SAM_OPEN_DOMAIN, "_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++) { + int len = doms[i] != NULL ? strlen(doms[i]) : 0; + + init_sam_entry(&sam[i], len, 0); + init_unistr2(&uni_name[i], doms[i], len); + } + + *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(info->acc_granted, SA_RIGHT_SAM_ENUM_DOMAINS, "_samr_enum_domains"))) { + return r_u->status; + } + + switch (lp_server_role()) { + case ROLE_DOMAIN_PDC: + case ROLE_DOMAIN_BDC: + name = lp_workgroup(); + break; + default: + name = lp_netbios_name(); + } + + fstrcpy(dom[0],name); + strupper(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 _api_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(acc_granted, SA_RIGHT_DOMAIN_OPEN_ACCOUNT, "_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->pipe_user.nt_user_token, + des_access, &acc_granted, "_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; +} + +/******************************************************************* + 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 + */ + + /* 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)) { + 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 ( (!IS_SAM_UNIX_USER(pwd)) || + ( (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() ) + if(!chgpasswd(pdb_get_username(pwd), "", plaintext_buf, True)) { + pdb_free_sam(&pwd); + return False; + } + } + + ZERO_STRUCT(plaintext_buf); + + 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)) { + 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 ( (!IS_SAM_UNIX_USER(pwd)) || + ( (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()) { + if(!chgpasswd(pdb_get_username(pwd), "", 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; + + 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(acc_granted, acc_required, "_samr_set_userinfo"))) { + return r_u->status; + } + + 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")); + 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)) + return NT_STATUS_ACCESS_DENIED; + break; + + case 24: + SamOEMhash(ctr->info.id24->pass, p->session_key, 516); + + dump_data(100, (char *)ctr->info.id24->pass, 516); + + if (!set_user_info_pw((char *)ctr->info.id24->pass, &sid)) + 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. + */ + + SamOEMhash(ctr->info.id25->pass, p->session_key, 532); + + 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 + return NT_STATUS_INVALID_INFO_CLASS; + + case 23: + SamOEMhash(ctr->info.id23->pass, p->session_key, 516); + + dump_data(100, (char *)ctr->info.id23->pass, 516); + + if (!set_user_info_23(ctr->info.id23, &sid)) + return NT_STATUS_ACCESS_DENIED; + break; + + default: + return NT_STATUS_INVALID_INFO_CLASS; + } + + 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; + + 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(acc_granted, acc_required, "_samr_set_userinfo2"))) { + return r_u->status; + } + + 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")); + 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)) + return NT_STATUS_ACCESS_DENIED; + break; + case 16: + if (!set_user_info_10(ctr->info.id10, &sid)) + return NT_STATUS_ACCESS_DENIED; + break; + case 18: + /* Used by AS/U JRA. */ + if (!set_user_info_12(ctr->info.id12, &sid)) + return NT_STATUS_ACCESS_DENIED; + break; + default: + return NT_STATUS_INVALID_INFO_CLASS; + } + + 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(info->acc_granted, SA_RIGHT_DOMAIN_LOOKUP_ALIAS_BY_MEM, "_samr_query_useraliases"); + ntstatus2 = access_check_samr_function(info->acc_granted, SA_RIGHT_DOMAIN_OPEN_ACCOUNT, "_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; inum_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; jalias_pol, &alias_sid, &acc_granted)) + return NT_STATUS_INVALID_HANDLE; + + if (!NT_STATUS_IS_OK(r_u->status = + access_check_samr_function(acc_granted, SA_RIGHT_ALIAS_GET_MEMBERS, "_samr_query_aliasmem"))) { + return r_u->status; + } + + sid_copy(&als_sid, &alias_sid); + sid_to_string(alias_sid_str, &alias_sid); + sid_split_rid(&alias_sid, &alias_rid); + + DEBUG(10, ("sid is %s\n", alias_sid_str)); + + if (sid_equal(&alias_sid, &global_sid_Builtin)) { + DEBUG(10, ("lookup on Builtin SID (S-1-5-32)\n")); + if(!get_builtin_group_from_sid(als_sid, &map, MAPPING_WITHOUT_PRIV)) + return NT_STATUS_NO_SUCH_ALIAS; + } else { + if (sid_equal(&alias_sid, get_global_sam_sid())) { + DEBUG(10, ("lookup on Server SID\n")); + if(!get_local_group_from_sid(als_sid, &map, MAPPING_WITHOUT_PRIV)) + return NT_STATUS_NO_SUCH_ALIAS; + } + } + + if(!get_uid_list_of_group(map.gid, &uid, &num_uids)) + return NT_STATUS_NO_SUCH_ALIAS; + + DEBUG(10, ("sid is %s\n", alias_sid_str)); + sid = (DOM_SID2 *)talloc_zero(p->mem_ctx, sizeof(DOM_SID2) * num_uids); + if (num_uids!=0 && sid == NULL) + return NT_STATUS_NO_MEMORY; + + for (i = 0; i < num_uids; i++) { + struct passwd *pass; + uint32 rid; + + sid_copy(&temp_sid, get_global_sam_sid()); + + pass = getpwuid_alloc(uid[i]); + if (!pass) continue; + + if (!NT_STATUS_IS_OK(pdb_init_sam(&sam_user))) { + passwd_free(&pass); + continue; + } + + become_root(); + check = pdb_getsampwnam(sam_user, pass->pw_name); + unbecome_root(); + + if (check != True) { + pdb_free_sam(&sam_user); + passwd_free(&pass); + continue; + } + + rid = pdb_get_user_rid(sam_user); + if (rid == 0) { + pdb_free_sam(&sam_user); + passwd_free(&pass); + continue; + } + + pdb_free_sam(&sam_user); + passwd_free(&pass); + + sid_append_rid(&temp_sid, rid); + + init_dom_sid2(&sid[i], &temp_sid); + } + + DEBUG(10, ("sid is %s\n", alias_sid_str)); + init_samr_r_query_aliasmem(r_u, num_uids, sid, NT_STATUS_OK); + + return NT_STATUS_OK; +} + +/********************************************************************* + _samr_query_groupmem +*********************************************************************/ + +NTSTATUS _samr_query_groupmem(pipes_struct *p, SAMR_Q_QUERY_GROUPMEM *q_u, SAMR_R_QUERY_GROUPMEM *r_u) +{ + int num_uids = 0; + int i; + DOM_SID group_sid; + uint32 group_rid; + fstring group_sid_str; + uid_t *uid=NULL; + + GROUP_MAP map; + + uint32 *rid=NULL; + uint32 *attr=NULL; + + SAM_ACCOUNT *sam_user = NULL; + BOOL check; + 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(acc_granted, SA_RIGHT_GROUP_GET_MEMBERS, "_samr_query_groupmem"))) { + return r_u->status; + } + + /* todo: change to use sid_compare_front */ + + sid_split_rid(&group_sid, &group_rid); + sid_to_string(group_sid_str, &group_sid); + DEBUG(10, ("sid is %s\n", group_sid_str)); + + /* can we get a query for an SID outside our domain ? */ + if (!sid_equal(&group_sid, get_global_sam_sid())) + return NT_STATUS_NO_SUCH_GROUP; + + sid_append_rid(&group_sid, group_rid); + DEBUG(10, ("lookup on Domain SID\n")); + + if(!get_domain_group_from_sid(group_sid, &map, MAPPING_WITHOUT_PRIV)) + return NT_STATUS_NO_SUCH_GROUP; + + if(!get_uid_list_of_group(map.gid, &uid, &num_uids)) + return NT_STATUS_NO_SUCH_GROUP; + + rid=talloc_zero(p->mem_ctx, sizeof(uint32)*num_uids); + attr=talloc_zero(p->mem_ctx, sizeof(uint32)*num_uids); + + if (num_uids!=0 && (rid==NULL || attr==NULL)) + return NT_STATUS_NO_MEMORY; + + for (i=0; ipw_name); + unbecome_root(); + + if (check != True) { + pdb_free_sam(&sam_user); + passwd_free(&pass); + continue; + } + + urid = pdb_get_user_rid(sam_user); + if (urid == 0) { + pdb_free_sam(&sam_user); + passwd_free(&pass); + continue; + } + + pdb_free_sam(&sam_user); + passwd_free(&pass); + + rid[i] = urid; + attr[i] = SID_NAME_USER; + } + + init_samr_r_query_groupmem(r_u, num_uids, 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; + fstring alias_sid_str; + uid_t uid; + struct passwd *pwd; + struct group *grp; + fstring grp_name; + GROUP_MAP map; + NTSTATUS ret; + SAM_ACCOUNT *sam_user = NULL; + BOOL check; + 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(acc_granted, SA_RIGHT_ALIAS_ADD_MEMBER, "_samr_add_aliasmem"))) { + return r_u->status; + } + + sid_to_string(alias_sid_str, &alias_sid); + DEBUG(10, ("sid is %s\n", alias_sid_str)); + + if (sid_compare(&alias_sid, get_global_sam_sid())>0) { + DEBUG(10, ("adding member on Server SID\n")); + if(!get_local_group_from_sid(alias_sid, &map, MAPPING_WITHOUT_PRIV)) + return NT_STATUS_NO_SUCH_ALIAS; + + } else { + if (sid_compare(&alias_sid, &global_sid_Builtin)>0) { + DEBUG(10, ("adding member on BUILTIN SID\n")); + if( !get_local_group_from_sid(alias_sid, &map, MAPPING_WITHOUT_PRIV)) + return NT_STATUS_NO_SUCH_ALIAS; + + } else + return NT_STATUS_NO_SUCH_ALIAS; + } + + ret = pdb_init_sam(&sam_user); + if (!NT_STATUS_IS_OK(ret)) + return ret; + + check = pdb_getsampwsid(sam_user, &q_u->sid.sid); + + if (check != True) { + pdb_free_sam(&sam_user); + return NT_STATUS_NO_SUCH_USER; + } + + uid = pdb_get_uid(sam_user); + if (uid == -1) { + pdb_free_sam(&sam_user); + return NT_STATUS_NO_SUCH_USER; + } + + pdb_free_sam(&sam_user); + + if ((pwd=getpwuid_alloc(uid)) == NULL) { + return NT_STATUS_NO_SUCH_USER; + } + + if ((grp=getgrgid(map.gid)) == NULL) { + passwd_free(&pwd); + return NT_STATUS_NO_SUCH_ALIAS; + } + + /* we need to copy the name otherwise it's overloaded in user_in_group_list */ + fstrcpy(grp_name, grp->gr_name); + + /* if the user is already in the group */ + if(user_in_unix_group_list(pwd->pw_name, grp_name)) { + passwd_free(&pwd); + return NT_STATUS_MEMBER_IN_ALIAS; + } + + /* + * 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, pwd->pw_name); + + /* check if the user has been added then ... */ + if(!user_in_unix_group_list(pwd->pw_name, grp_name)) { + passwd_free(&pwd); + return NT_STATUS_MEMBER_NOT_IN_ALIAS; /* don't know what to reply else */ + } + + passwd_free(&pwd); + 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; + fstring alias_sid_str; + struct group *grp; + fstring grp_name; + GROUP_MAP map; + SAM_ACCOUNT *sam_pass=NULL; + 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(acc_granted, SA_RIGHT_ALIAS_REMOVE_MEMBER, "_samr_del_aliasmem"))) { + return r_u->status; + } + + sid_to_string(alias_sid_str, &alias_sid); + DEBUG(10, ("_samr_del_aliasmem:sid is %s\n", alias_sid_str)); + + if (!sid_check_is_in_our_domain(&alias_sid) && + !sid_check_is_in_builtin(&alias_sid)) { + DEBUG(10, ("_samr_del_aliasmem:invalid alias group\n")); + return NT_STATUS_NO_SUCH_ALIAS; + } + + if( !get_local_group_from_sid(alias_sid, &map, MAPPING_WITHOUT_PRIV)) + return NT_STATUS_NO_SUCH_ALIAS; + + if ((grp=getgrgid(map.gid)) == NULL) + return NT_STATUS_NO_SUCH_ALIAS; + + /* we need to copy the name otherwise it's overloaded in user_in_unix_group_list */ + fstrcpy(grp_name, grp->gr_name); + + /* check if the user exists before trying to remove it from the group */ + pdb_init_sam(&sam_pass); + if(!pdb_getsampwsid(sam_pass, &q_u->sid.sid)) { + DEBUG(5,("_samr_del_aliasmem:User %s doesn't exist.\n", pdb_get_username(sam_pass))); + pdb_free_sam(&sam_pass); + return NT_STATUS_NO_SUCH_USER; + } + + /* if the user is not in the group */ + if(!user_in_unix_group_list(pdb_get_username(sam_pass), grp_name)) { + pdb_free_sam(&sam_pass); + return NT_STATUS_MEMBER_IN_ALIAS; + } + + smb_delete_user_group(grp_name, pdb_get_username(sam_pass)); + + /* check if the user has been removed then ... */ + if(user_in_unix_group_list(pdb_get_username(sam_pass), grp_name)) { + pdb_free_sam(&sam_pass); + return NT_STATUS_MEMBER_NOT_IN_ALIAS; /* don't know what to reply else */ + } + + pdb_free_sam(&sam_pass); + 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 group_sid_str; + struct passwd *pwd; + struct group *grp; + fstring grp_name; + GROUP_MAP map; + uid_t uid; + NTSTATUS ret; + SAM_ACCOUNT *sam_user=NULL; + BOOL check; + 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(acc_granted, SA_RIGHT_GROUP_ADD_MEMBER, "_samr_add_groupmem"))) { + return r_u->status; + } + + sid_to_string(group_sid_str, &group_sid); + DEBUG(10, ("sid is %s\n", group_sid_str)); + + if (sid_compare(&group_sid, get_global_sam_sid())<=0) + return NT_STATUS_NO_SUCH_GROUP; + + DEBUG(10, ("lookup on Domain SID\n")); + + if(!get_domain_group_from_sid(group_sid, &map, MAPPING_WITHOUT_PRIV)) + return NT_STATUS_NO_SUCH_GROUP; + + sid_copy(&user_sid, get_global_sam_sid()); + sid_append_rid(&user_sid, q_u->rid); + + ret = pdb_init_sam(&sam_user); + if (!NT_STATUS_IS_OK(ret)) + return ret; + + check = pdb_getsampwsid(sam_user, &user_sid); + + if (check != True) { + pdb_free_sam(&sam_user); + return NT_STATUS_NO_SUCH_USER; + } + + uid = pdb_get_uid(sam_user); + if (uid == -1) { + pdb_free_sam(&sam_user); + return NT_STATUS_NO_SUCH_USER; + } + + pdb_free_sam(&sam_user); + + if ((pwd=getpwuid_alloc(uid)) == NULL) { + return NT_STATUS_NO_SUCH_USER; + } + + if ((grp=getgrgid(map.gid)) == NULL) { + passwd_free(&pwd); + return NT_STATUS_NO_SUCH_GROUP; + } + + /* we need to copy the name otherwise it's overloaded in user_in_unix_group_list */ + fstrcpy(grp_name, grp->gr_name); + + /* if the user is already in the group */ + if(user_in_unix_group_list(pwd->pw_name, grp_name)) { + passwd_free(&pwd); + 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, pwd->pw_name); + + /* check if the user has been added then ... */ + if(!user_in_unix_group_list(pwd->pw_name, grp_name)) { + passwd_free(&pwd); + return NT_STATUS_MEMBER_NOT_IN_GROUP; /* don't know what to reply else */ + } + + passwd_free(&pwd); + 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; + SAM_ACCOUNT *sam_pass=NULL; + GROUP_MAP map; + fstring grp_name; + struct group *grp; + 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(acc_granted, SA_RIGHT_GROUP_REMOVE_MEMBER, "_samr_del_groupmem"))) { + return r_u->status; + } + + if (!sid_check_is_in_our_domain(&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 (!get_domain_group_from_sid(group_sid, &map, MAPPING_WITHOUT_PRIV)) + return NT_STATUS_NO_SUCH_GROUP; + + if ((grp=getgrgid(map.gid)) == NULL) + return NT_STATUS_NO_SUCH_GROUP; + + /* we need to copy the name otherwise it's overloaded in user_in_group_list */ + fstrcpy(grp_name, grp->gr_name); + + /* check if the user exists before trying to remove it from the group */ + pdb_init_sam(&sam_pass); + if (!pdb_getsampwsid(sam_pass, &user_sid)) { + DEBUG(5,("User %s doesn't exist.\n", pdb_get_username(sam_pass))); + pdb_free_sam(&sam_pass); + return NT_STATUS_NO_SUCH_USER; + } + + /* if the user is not in the group */ + if (!user_in_unix_group_list(pdb_get_username(sam_pass), grp_name)) { + pdb_free_sam(&sam_pass); + return NT_STATUS_MEMBER_NOT_IN_GROUP; + } + + smb_delete_user_group(grp_name, pdb_get_username(sam_pass)); + + /* check if the user has been removed then ... */ + if (user_in_unix_group_list(pdb_get_username(sam_pass), grp_name)) { + pdb_free_sam(&sam_pass); + return NT_STATUS_ACCESS_DENIED; /* don't know what to reply else */ + } + + pdb_free_sam(&sam_pass); + return NT_STATUS_OK; + +} + +/**************************************************************************** + Delete a UNIX user on demand. +****************************************************************************/ + +static int smb_delete_user(const char *unix_user) +{ + pstring del_script; + int ret; + + 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(acc_granted, STD_RIGHT_DELETE_ACCESS, "_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", pdb_get_username(sam_pass))); + 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; + DOM_SID dom_sid; + uint32 group_rid; + fstring group_sid_str; + gid_t gid; + struct group *grp; + GROUP_MAP map; + 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(acc_granted, STD_RIGHT_DELETE_ACCESS, "_samr_delete_dom_group"))) { + return r_u->status; + } + + sid_copy(&dom_sid, &group_sid); + sid_to_string(group_sid_str, &dom_sid); + sid_split_rid(&dom_sid, &group_rid); + + DEBUG(10, ("sid is %s\n", group_sid_str)); + + /* we check if it's our SID before deleting */ + if (!sid_equal(&dom_sid, get_global_sam_sid())) + return NT_STATUS_NO_SUCH_GROUP; + + DEBUG(10, ("lookup on Domain SID\n")); + + if(!get_domain_group_from_sid(group_sid, &map, MAPPING_WITHOUT_PRIV)) + return NT_STATUS_NO_SUCH_GROUP; + + gid=map.gid; + + /* check if group really exists */ + if ( (grp=getgrgid(gid)) == NULL) + return NT_STATUS_NO_SUCH_GROUP; + + /* we can delete the UNIX group */ + smb_delete_group(grp->gr_name); + + /* check if the group has been successfully deleted */ + if ( (grp=getgrgid(gid)) != NULL) + return NT_STATUS_ACCESS_DENIED; + + if(!pdb_delete_group_mapping_entry(group_sid)) + 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; + DOM_SID dom_sid; + uint32 alias_rid; + fstring alias_sid_str; + gid_t gid; + struct group *grp; + GROUP_MAP map; + 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(acc_granted, STD_RIGHT_DELETE_ACCESS, "_samr_delete_dom_alias"))) { + return r_u->status; + } + + sid_copy(&dom_sid, &alias_sid); + sid_to_string(alias_sid_str, &dom_sid); + sid_split_rid(&dom_sid, &alias_rid); + + DEBUG(10, ("sid is %s\n", alias_sid_str)); + + /* we check if it's our SID before deleting */ + if (!sid_equal(&dom_sid, get_global_sam_sid())) + return NT_STATUS_NO_SUCH_ALIAS; + + DEBUG(10, ("lookup on Local SID\n")); + + if(!get_local_group_from_sid(alias_sid, &map, MAPPING_WITHOUT_PRIV)) + return NT_STATUS_NO_SUCH_ALIAS; + + gid=map.gid; + + /* check if group really exists */ + if ( (grp=getgrgid(gid)) == NULL) + return NT_STATUS_NO_SUCH_ALIAS; + + /* we can delete the UNIX group */ + smb_delete_group(grp->gr_name); + + /* check if the group has been successfully deleted */ + if ( (grp=getgrgid(gid)) != NULL) + return NT_STATUS_ACCESS_DENIED; + + /* don't check if we removed it as it could be an un-mapped group */ + pdb_delete_group_mapping_entry(alias_sid); + + 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; + PRIVILEGE_SET priv_set; + uint32 acc_granted; + gid_t gid; + + init_privilege(&priv_set); + + /* 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(acc_granted, SA_RIGHT_DOMAIN_CREATE_GROUP, "_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, priv_set, PR_ACCESS_FROM_NETWORK)) + 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; + fstring sid_string; + struct group *grp; + struct samr_info *info; + PRIVILEGE_SET priv_set; + uint32 acc_granted; + gid_t gid; + + init_privilege(&priv_set); + + /* 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(acc_granted, SA_RIGHT_DOMAIN_CREATE_ALIAS, "_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); + + /* check if group already exists */ + 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); + + sid_copy(&info_sid, get_global_sam_sid()); + sid_append_rid(&info_sid, r_u->rid); + sid_to_string(sid_string, &info_sid); + + /* add the group to the mapping table */ + if(!add_initial_entry(grp->gr_gid, sid_string, SID_NAME_ALIAS, name, NULL, priv_set, PR_ACCESS_FROM_NETWORK)) + 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; + GROUP_MAP map; + uid_t *uid=NULL; + int num_uids=0; + GROUP_INFO_CTR *ctr; + uint32 acc_granted; + + 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(acc_granted, SA_RIGHT_GROUP_LOOKUP_INFO, "_samr_query_groupinfo"))) { + return r_u->status; + } + + if (!get_domain_group_from_sid(group_sid, &map, MAPPING_WITHOUT_PRIV)) + 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_uid_list_of_group(map.gid, &uid, &num_uids)) + return NT_STATUS_NO_SUCH_GROUP; + init_samr_group_info1(&ctr->group.info1, map.nt_name, map.comment, num_uids); + SAFE_FREE(uid); + 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, map.comment); + 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_MAP map; + GROUP_INFO_CTR *ctr; + uint32 acc_granted; + + 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(acc_granted, SA_RIGHT_GROUP_SET_INFO, "_samr_set_groupinfo"))) { + return r_u->status; + } + + if (!get_domain_group_from_sid(group_sid, &map, MAPPING_WITH_PRIV)) + return NT_STATUS_NO_SUCH_GROUP; + + ctr=q_u->ctr; + + switch (ctr->switch_value1) { + case 1: + unistr2_to_ascii(map.comment, &(ctr->group.info1.uni_acct_desc), sizeof(map.comment)-1); + break; + case 4: + unistr2_to_ascii(map.comment, &(ctr->group.info4.uni_acct_desc), sizeof(map.comment)-1); + break; + default: + free_privilege(&map.priv_set); + return NT_STATUS_INVALID_INFO_CLASS; + } + + if(!pdb_update_group_mapping_entry(&map)) { + free_privilege(&map.priv_set); + return NT_STATUS_NO_SUCH_GROUP; + } + + free_privilege(&map.priv_set); + + 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; + GROUP_MAP map; + 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(acc_granted, SA_RIGHT_ALIAS_SET_INFO, "_samr_set_aliasinfo"))) { + return r_u->status; + } + + if (!get_local_group_from_sid(group_sid, &map, MAPPING_WITH_PRIV)) + return NT_STATUS_NO_SUCH_GROUP; + + ctr=&q_u->ctr; + + switch (ctr->switch_value1) { + case 3: + unistr2_to_ascii(map.comment, &(ctr->alias.info3.uni_acct_desc), sizeof(map.comment)-1); + break; + default: + free_privilege(&map.priv_set); + return NT_STATUS_INVALID_INFO_CLASS; + } + + if(!pdb_update_group_mapping_entry(&map)) { + free_privilege(&map.priv_set); + return NT_STATUS_NO_SUCH_GROUP; + } + + free_privilege(&map.priv_set); + + 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; + GROUP_MAP map; + struct samr_info *info; + SEC_DESC *psd = NULL; + uint32 acc_granted; + uint32 des_access; + 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(acc_granted, SA_RIGHT_DOMAIN_OPEN_ACCOUNT, "_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->pipe_user.nt_user_token, + des_access, &acc_granted, "_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)); + + /* check if that group really exists */ + if (!get_domain_group_from_sid(info->sid, &map, MAPPING_WITHOUT_PRIV)) + 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_unknown_2d +*********************************************************************/ + +NTSTATUS _samr_unknown_2d(pipes_struct *p, SAMR_Q_UNKNOWN_2D *q_u, SAMR_R_UNKNOWN_2D *r_u) +{ + DEBUG(0,("_samr_unknown_2d: Not yet implemented.\n")); + return NT_STATUS_NOT_IMPLEMENTED; +} + +/******************************************************************* + _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(), lp_netbios_name(), (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, lp_netbios_name()); + 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; + + account_policy_get(AP_RESET_COUNT_TIME, &account_policy_temp); + u_reset_time = account_policy_temp; + + 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); + u_reset_time=nt_time_to_unix_abs(&q_u->ctr->info.inf12.reset_count); + + 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/source4/rpc_server/srv_samr_util.c b/source4/rpc_server/srv_samr_util.c new file mode 100644 index 0000000000..d7ead0d15f --- /dev/null +++ b/source4/rpc_server/srv_samr_util.c @@ -0,0 +1,437 @@ +/* + 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)) + +/************************************************************* + 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; + + if (from == NULL || to == NULL) + return; + if (!nt_time_is_zero(&from->logon_time)) { + 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 (!nt_time_is_zero(&from->logoff_time)) { + 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 (!nt_time_is_zero(&from->kickoff_time)) { + 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 (!nt_time_is_zero(&from->pass_can_change_time)) { + 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 (!nt_time_is_zero(&from->pass_last_set_time)) { + 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 (!nt_time_is_zero(&from->pass_must_change_time)) { + 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); + } + + /* Backend should check this for sainity */ + if (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->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->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->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->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->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->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->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); + } + + if (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->hdr_munged_dial.buffer) { + old_string = pdb_get_munged_dial(to); + new_string = unistr2_static(&from->uni_munged_dial); + DEBUG(10,("INFO_21 UNI_MUNGED_DIAL: %s -> %s\n",old_string, new_string)); + if (STRING_CHANGED) + pdb_set_munged_dial(to , new_string, PDB_CHANGED); + } + + 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)); + /* we really allow this ??? metze */ + /* pdb_set_user_sid_from_rid(to, from->user_rid, PDB_CHANGED);*/ + } + + 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); + } + + 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); + } + + DEBUG(10,("INFO_21 UNKOWN_3: %08X -> %08X\n",pdb_get_unknown_3(to),from->unknown_3)); + if (from->unknown_3 != pdb_get_unknown_3(to)) { + pdb_set_unknown_3(to, from->unknown_3, PDB_CHANGED); + } + + 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); + + DEBUG(10,("INFO_21 UNKOWN_5: %08X -> %08X\n",pdb_get_unknown_5(to),from->unknown_5)); + if (from->unknown_5 != pdb_get_unknown_5(to)) { + pdb_set_unknown_5(to, from->unknown_5, PDB_CHANGED); + } + + DEBUG(10,("INFO_21 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); + } + + DEBUG(10,("INFO_21 PADDING1 %02X %02X %02X %02X %02X %02X\n", + from->padding1[0], + from->padding1[1], + from->padding1[2], + from->padding1[3], + from->padding1[4], + from->padding1[5])); + + 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); + } + + 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; + + if (from == NULL || to == NULL) + return; + if (!nt_time_is_zero(&from->logon_time)) { + 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 (!nt_time_is_zero(&from->logoff_time)) { + 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 (!nt_time_is_zero(&from->kickoff_time)) { + 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 (!nt_time_is_zero(&from->pass_can_change_time)) { + 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 (!nt_time_is_zero(&from->pass_last_set_time)) { + 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 (!nt_time_is_zero(&from->pass_must_change_time)) { + 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 sainity */ + if (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->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->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->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->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->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->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->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); + } + + if (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->hdr_munged_dial.buffer) { + old_string = pdb_get_munged_dial(to); + new_string = unistr2_static(&from->uni_munged_dial); + DEBUG(10,("INFO_23 UNI_MUNGED_DIAL: %s -> %s\n",old_string, new_string)); + if (STRING_CHANGED) + pdb_set_munged_dial(to , new_string, PDB_CHANGED); + } + + 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)); + /* we really allow this ??? metze */ + /* pdb_set_user_sid_from_rid(to, from->user_rid, PDB_CHANGED);*/ + } + + 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); + } + + 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); + } + + DEBUG(10,("INFO_23 UNKOWN_3: %08X -> %08X\n",pdb_get_unknown_3(to),from->unknown_3)); + if (from->unknown_3 != pdb_get_unknown_3(to)) { + pdb_set_unknown_3(to, from->unknown_3, PDB_CHANGED); + } + + 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); + + DEBUG(10,("INFO_23 UNKOWN_5: %08X -> %08X\n",pdb_get_unknown_5(to),from->unknown_5)); + if (from->unknown_5 != pdb_get_unknown_5(to)) { + pdb_set_unknown_5(to, from->unknown_5, PDB_CHANGED); + } + + 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); + } + + DEBUG(10,("INFO_23 PADDING1 %02X %02X %02X %02X %02X %02X\n", + from->padding1[0], + from->padding1[1], + from->padding1[2], + from->padding1[3], + from->padding1[4], + from->padding1[5])); + + 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); + } + + DEBUG(10,("INFO_23 PADDING_2: %02X\n",from->padding2)); + + DEBUG(10,("INFO_23 PADDING_4: %08X\n",from->padding4)); +} + + diff --git a/source4/rpc_server/srv_spoolss.c b/source4/rpc_server/srv_spoolss.c new file mode 100755 index 0000000000..3023922a5b --- /dev/null +++ b/source4/rpc_server/srv_spoolss.c @@ -0,0 +1,1649 @@ +/* + * 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) Anthony Liguori 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 +********************************************************************/ + +#ifdef RPC_SPOOLSS_DYNAMIC +int init_module(void) +#else +int rpc_spoolss_init(void) +#endif +{ + 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 + }; + return rpc_pipe_register_commands("spoolss", "spoolss", api_spoolss_cmds, + sizeof(api_spoolss_cmds) / sizeof(struct api_struct)); +} diff --git a/source4/rpc_server/srv_spoolss_nt.c b/source4/rpc_server/srv_spoolss_nt.c new file mode 100644 index 0000000000..0c29962008 --- /dev/null +++ b/source4/rpc_server/srv_spoolss_nt.c @@ -0,0 +1,9079 @@ +/* + * 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 */ +}; + +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(¬ify_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(¬ify_cli); + cli_ulogoff(¬ify_cli); + cli_shutdown(¬ify_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; +} + +/**************************************************************************** + find printer index by handle +****************************************************************************/ + +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; + int i; + + /* 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 ( ( i = 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=%d)\n", handlename, 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=%d)\n", aprinter, 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 %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 ( !(new_printer->ctx = talloc_init("Printer Entry [0x%x]", (uint32)hnd)) ) { + DEBUG(0,("open_printer_hnd: talloc_init() failed!\n")); + return False; + } + + new_printer->notify.option=NULL; + + /* Add to the internal list. */ + DLIST_ADD(printers_list, new_printer); + + if (!create_policy_hnd(p, hnd, free_printer_entry, new_printer)) { + SAFE_FREE(new_printer); + 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, msg->len); + + 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; inum_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; + + + 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, event_index; + + /* 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); + + event_index = 0; + + /* build the array of change notifications */ + + for ( i=0; inum_msgs; i++ ) { + SPOOLSS_NOTIFY_MSG *msg = &messages[i]; + + /* Are we monitoring this event? */ + + if (!is_monitoring_event(p, msg->type, msg->field)) + continue; + + + 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++; + } + + cli_spoolss_rrpcn( ¬ify_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, void *buf, size_t len ) +{ + + 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, "ddddd", + &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, type %d, field 0x%02x, flags 0x%04x\n", + msg->type, msg->field, msg->flags)); + + 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 %d messages in list\n", 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 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( ¬ify, msg_ptr, msg_len ); + msg_ptr += msg_len; + + /* add to correct list in container */ + + notify_msg_ctr_addmsg( &messages, ¬ify ); + + /* 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; iinfo_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 cahce 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; snuminfo_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)) + 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 ( RA_WIN2K == get_remote_arch() ) + usleep( 384000 ); + + 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; +#ifdef HAVE_ADS + SIVAL(*data, 0, 3); +#else + SIVAL(*data, 0, 2); +#endif + *needed = 0x4; + return WERR_OK; + } + + if (!StrCaseCmp(value, "DefaultSpoolDirectory")) { + fstring string; + + fstrcpy(string, string_truncate(lp_serverstring(), MAX_SERVER_STRING_LENGTH)); + *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 in_size) ? *needed:in_size) *sizeof(uint8))) == NULL) + return WERR_NOMEM; + memset(*data, 0, (*needed > in_size) ? *needed:in_size); + for (i=0; i in_size) ? *needed:in_size) *sizeof(uint8))) == NULL) + return WERR_NOMEM; + memset(*data, 0, (*needed > in_size) ? *needed:in_size); + for (i=0; ihandle; + 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; + } + **data = 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, const char *remote_machine) +{ + ZERO_STRUCTP(the_cli); + if(cli_initialise(the_cli) == NULL) { + DEBUG(0,("connect_to_client: unable to initialize client connection.\n")); + return False; + } + + if(!resolve_name( remote_machine, &the_cli->dest_ip, 0x20)) { + DEBUG(0,("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,("connect_to_client: Machine %s is one of our addresses. Cannot add to ourselves.\n", remote_machine)); + cli_shutdown(the_cli); + return False; + } + + if (!cli_connect(the_cli, remote_machine, &the_cli->dest_ip)) { + DEBUG(0,("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, lp_netbios_name(), remote_machine, &the_cli->dest_ip)) { + DEBUG(0,("connect_to_client: machine %s rejected the NetBIOS session request.\n", + remote_machine)); + cli_shutdown(the_cli); + return False; + } + + the_cli->protocol = PROTOCOL_NT1; + + if (!cli_negprot(the_cli)) { + DEBUG(0,("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,("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,("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,("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,("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,("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) +{ + WERROR result; + + /* + * If it's the first connection, contact the client + * and connect to the IPC$ share anonumously + */ + if (smb_connections==0) { + fstring unix_printer; + + fstrcpy(unix_printer, printer+2); /* the +2 is to strip the leading 2 backslashs */ + + if(!spoolss_connect_to_client(¬ify_cli, 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(¬ify_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; + + /* 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; + + if(!srv_spoolss_replyopenprinter(snum, Printer->notify.localmachine, + Printer->notify.printerlocal, 1, + &Printer->notify.client_hnd)) + 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_numcount; 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_numcount; 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; + uint32 id; + 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; + id=1; + info->version=2; + info->data=NULL; + info->count=0; + + for (i=0; icount; i++) { + option_type=&(option->ctr.type[i]); + + if (option_type->type!=PRINTER_NOTIFY_TYPE) + continue; + + for (snum=0; snumversion:[%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; icount; 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; icount; 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; jversion:[%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; icount; 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; +#ifdef HAVE_ADS + printer->major_version = 0x0005; /* NT 5 */ + printer->build_version = 0x0893; /* build 2195 */ +#else + printer->major_version = 0x0004; /* NT 4 */ + printer->build_version = 0x0565; /* build 1381 */ +#endif + 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; + GUID guid; + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_init("dump_guid"); + if (!mem_ctx) return; + + if (is_printer_published(print_hnd, snum, &guid)) { + asprintf(&guid_str, "{%s}", uuid_string(mem_ctx, guid)); + strupper(guid_str); + init_unistr(&printer->guid, guid_str); + printer->action = SPOOL_DS_PUBLISH; + } else { + init_unistr(&printer->guid, ""); + printer->action = SPOOL_DS_UNPUBLISH; + } + talloc_destroy(mem_ctx); + + 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 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 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(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:%d\n", i, line, 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; + } + + /* 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, strlen(printer->info_2->comment)+1 ); + 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, strlen(printer->info_2->sharename)+1 ); + 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, strlen(printer->info_2->portname)+1 ); + 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, strlen(printer->info_2->location)+1 ); + 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, lp_netbios_name(), strlen(lp_netbios_name())+1 ); + 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", + lp_netbios_name(), printer->info_2->sharename ); + init_unistr2( &buffer, asc_buffer, strlen(asc_buffer)+1 ); + 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); + WERROR result; + + DEBUG(5,("publish_or_unpublish_printer, action = %d\n",info7->action)); + + result = WERR_OK; + + 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; + + int snum; + print_status_struct prt_status; + print_queue_struct *queue=NULL; + int max_rep_jobs; + + /* 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; + + max_rep_jobs = lp_max_reported_jobs(snum); + + *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; + } + + if (max_rep_jobs && (*returned > max_rep_jobs)) + *returned = max_rep_jobs; + + switch (level) { + case 1: + return enumjobs_level1(queue, snum, buffer, offered, needed, returned); + case 2: + return enumjobs_level2(queue, snum, buffer, offered, needed, returned); + 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 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 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 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 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 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"); +#define PORT_TYPE_WRITE 1 + 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 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 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; + } + + 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: + sys_adminlog(LOG_INFO,"Added printer driver. Print driver name: %s. Print driver OS: %s. Administrator name: %s.", + driver.info_3->name,drv_ver_to_os[driver.info_3->cversion],uidtoname(user.uid)); + fstrcpy(driver_name, driver.info_3->name); + break; + case 6: + sys_adminlog(LOG_INFO,"Added printer driver. Print driver name: %s. Print driver OS: %s. Administrator name: %s.", + driver.info_6->name,drv_ver_to_os[driver.info_6->version],uidtoname(user.uid)); + fstrcpy(driver_name, driver.info_6->name); + 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; + pstring short_archi; + DRIVER_DIRECTORY_1 *info=NULL; + + unistr2_to_ascii(long_archi, uni_environment, sizeof(long_archi)-1); + + if (get_short_archi(short_archi, long_archi)==False) + 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 param_index; + 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; + 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 ) + { + DEBUGADD(6,("Activating NT mega-hack to find sizes\n")); + + param_index = 0; + biggest_valuesize = 0; + biggest_datasize = 0; + + num_values = regval_ctr_numvals( &p_data->keys[key_index].values ); + + for ( i=0; ikeys[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 + */ + + 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 (!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 ); + + 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) { + SAFE_FREE(queue); + return WERR_NOMEM; + } + + for (i=0; i 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 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_getprinterdatex: 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_setprinterdata: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle))); + return WERR_BADFID; + } + + 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 ); + + 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 [%d] bytes!\n", + 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; ikeys[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; + pstring short_archi; + PRINTPROCESSOR_DIRECTORY_1 *info=NULL; + + unistr2_to_ascii(long_archi, environment, sizeof(long_archi)-1); + + if (get_short_archi(short_archi, long_archi)==False) + 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/source4/rpc_server/srv_srvsvc.c b/source4/rpc_server/srv_srvsvc.c new file mode 100644 index 0000000000..7c5e317c87 --- /dev/null +++ b/source4/rpc_server/srv_srvsvc.c @@ -0,0 +1,557 @@ +/* + * 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) Anthony Liguori 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 +********************************************************************/ + +#ifdef RPC_SVC_DYNAMIC +int init_module(void) +#else +int rpc_srv_init(void) +#endif +{ + static const 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 } + }; + return rpc_pipe_register_commands("srvsvc", "ntsvcs", api_srv_cmds, + sizeof(api_srv_cmds) / sizeof(struct api_struct)); +} diff --git a/source4/rpc_server/srv_srvsvc_nt.c b/source4/rpc_server/srv_srvsvc_nt.c new file mode 100644 index 0000000000..44a63f2b85 --- /dev/null +++ b/source4/rpc_server/srv_srvsvc_nt.c @@ -0,0 +1,2138 @@ +/* + * 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, 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(struct request_context *req, struct tcon_context *conn, int snum, uint32 desired_access) +{ + uint32 granted; + NTSTATUS status; + SEC_DESC *psd = NULL; + size_t sd_size; + NT_USER_TOKEN *token = NULL; + BOOL ret = True; + struct tcon_context *conn = req->conn; + + psd = get_share_security(req->mem_ctx, snum, &sd_size); + + if (!psd) + goto out; + + if (conn->nt_user_token) + token = conn->nt_user_token; + else + token = req->user_ctx->nt_user_token; + + ret = se_access_check(psd, token, desired_access, &granted, &status); + + 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->dfs_root_flag = 0; + + if(lp_host_msdfs() && lp_msdfs_root(snum)) + sh1005->dfs_root_flag = 3; +} +/*************************************************************************** + 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) +{ + pstring net_name; + + pstrcpy(net_name, lp_servicename(snum)); + return (net_name[strlen(net_name)] == '$') ? 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 ;inum_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, lp_netbios_name(), + 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, lp_netbios_name(), + 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, lp_netbios_name()); + 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) +{ + pstring saved_pathname; + pstring unix_pathname; + char *ptr; + int ret; + + /* 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; + + /* Can we cd to it ? */ + + /* First save our current directory. */ + if (getcwd(saved_pathname, sizeof(saved_pathname)) == NULL) + return False; + + pstrcpy(unix_pathname, ptr); + + 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"); + + return (ret != -1) ? ptr : NULL; +} + +/******************************************************************* + 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 *ptr; + 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: + 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 (!(ptr = 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(ptr, '"', ' '); + 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(ptr, 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, ptr, 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; + } + + /* 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 *ptr; + 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 (!(ptr = 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(ptr, '"', ' '); + string_replace(comment, '"', ' '); + + slprintf(command, sizeof(command)-1, "%s \"%s\" \"%s\" \"%s\" \"%s\"", + lp_add_share_cmd(), dyn_CONFIGFILE, share_name, ptr, 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; + } + + 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; + struct tcon_context *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), 0, 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), 0, &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 = conn->vfs_ops.get_nt_acl(fsp, fsp->fsp_name, &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; + struct tcon_context *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), 0, 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), 0, &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 = conn->vfs_ops.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" . +***********************************************************************************/ + +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/source4/rpc_server/srv_util.c b/source4/rpc_server/srv_util.c new file mode 100644 index 0000000000..4eba9c7d1f --- /dev/null +++ b/source4/rpc_server/srv_util.c @@ -0,0 +1,546 @@ +/* + * 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_winbind_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); + gid=pdb_get_gid(sam_pass); + + 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= 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; + } + + free(groups); + + /* now check for the user's gid (the primary group rid) */ + for (i=0; i= 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; + + *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)); + + /* first get the list of the domain groups */ + if (!pdb_enum_group_mapping(SID_NAME_DOM_GRP, &map, &num_entries, ENUM_ONLY_MAPPED, MAPPING_WITHOUT_PRIV)) + 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*/ + for(i=0; igr_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; inum_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/source4/rpc_server/srv_wkssvc.c b/source4/rpc_server/srv_wkssvc.c new file mode 100644 index 0000000000..e0d662ea80 --- /dev/null +++ b/source4/rpc_server/srv_wkssvc.c @@ -0,0 +1,75 @@ +/* + * 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) Anthony Liguori 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 + ********************************************************************/ + +#ifdef RPC_WKS_DYNAMIC +int init_module(void) +#else +int rpc_wks_init(void) +#endif +{ + static struct api_struct api_wks_cmds[] = + { + { "WKS_Q_QUERY_INFO", WKS_QUERY_INFO, api_wks_query_info } + }; + return rpc_pipe_register_commands("wkssvc", "ntsvcs", api_wks_cmds, + sizeof(api_wks_cmds) / sizeof(struct api_struct)); +} diff --git a/source4/rpc_server/srv_wkssvc_nt.c b/source4/rpc_server/srv_wkssvc_nt.c new file mode 100644 index 0000000000..2ca43e5d51 --- /dev/null +++ b/source4/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, lp_netbios_name()); + strupper(my_name); + + pstrcpy (domain, lp_workgroup()); + strupper(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/source4/rpcclient/cmd_dfs.c b/source4/rpcclient/cmd_dfs.c new file mode 100644 index 0000000000..715174c824 --- /dev/null +++ b/source4/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", cmd_dfs_exist, PI_NETDFS, "Query DFS support", "" }, + { "dfsadd", cmd_dfs_add, PI_NETDFS, "Add a DFS share", "" }, + { "dfsremove", cmd_dfs_remove, PI_NETDFS, "Remove a DFS share", "" }, + { "dfsgetinfo", cmd_dfs_getinfo, PI_NETDFS, "Query DFS share info", "" }, + { "dfsenum", cmd_dfs_enum, PI_NETDFS, "Enumerate dfs shares", "" }, + + { NULL } +}; diff --git a/source4/rpcclient/cmd_ds.c b/source4/rpcclient/cmd_ds.c new file mode 100644 index 0000000000..9de6d6a9ec --- /dev/null +++ b/source4/rpcclient/cmd_ds.c @@ -0,0 +1,59 @@ +/* + 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; +} + +/* List of commands exported by this module */ + +struct cmd_set ds_commands[] = { + + { "LSARPC-DS" }, + + { "dsroledominfo", cmd_ds_dsrole_getprimarydominfo, PI_LSARPC_DS, "Get Primary Domain Information", "" }, + + { NULL } +}; diff --git a/source4/rpcclient/cmd_lsarpc.c b/source4/rpcclient/cmd_lsarpc.c new file mode 100644 index 0000000000..fab6a89ed5 --- /dev/null +++ b/source4/rpcclient/cmd_lsarpc.c @@ -0,0 +1,760 @@ +/* + 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; + GUID dom_guid; + fstring sid_str, domain_name="", dns_name="", forest_name=""; + 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[0]) + 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[0]) + printf("domain dns name is %s\n", dns_name); + if (forest_name[0]) + printf("forest name is %s\n", forest_name); + + if (info_class == 12) { + printf("domain GUID is "); + print_guid(&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; +} + +/* 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; +} + + +/* Enumerate the accounts with a specific right */ + +static NTSTATUS cmd_lsa_enum_acct_with_right(struct cli_state *cli, + TALLOC_CTX *mem_ctx, int argc, + const char **argv) +{ + POLICY_HND dom_pol; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + DOM_SID *sids; + uint32 count; + const char *right; + + int i; + + if (argc != 2 ) { + printf("Usage: %s \n", argv[0]); + return NT_STATUS_OK; + } + + right = argv[1]; + + 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_with_right(cli, mem_ctx, &dom_pol, right, &count, &sids); + + if (!NT_STATUS_IS_OK(result)) + goto done; + + printf("found %d SIDs for '%s'\n", count, right); + + for (i = 0; i < count; i++) { + printf("\t%s\n", sid_string_static(&sids[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", cmd_lsa_query_info_policy, PI_LSARPC, "Query info policy", "" }, + { "lookupsids", cmd_lsa_lookup_sids, PI_LSARPC, "Convert SIDs to names", "" }, + { "lookupnames", cmd_lsa_lookup_names, PI_LSARPC, "Convert names to SIDs", "" }, + { "enumtrust", cmd_lsa_enum_trust_dom, PI_LSARPC, "Enumerate trusted domains", "Usage: [preferred max number] [enum context (0)]" }, + { "enumprivs", cmd_lsa_enum_privilege, PI_LSARPC, "Enumerate privileges", "" }, + { "getdispname", cmd_lsa_get_dispname, PI_LSARPC, "Get the privilege name", "" }, + { "lsaenumsid", cmd_lsa_enum_sids, PI_LSARPC, "Enumerate the LSA SIDS", "" }, + { "lsaenumprivsaccount", cmd_lsa_enum_privsaccounts, PI_LSARPC, "Enumerate the privileges of an SID", "" }, + { "lsaenumacctrights", cmd_lsa_enum_acct_rights, PI_LSARPC, "Enumerate the rights of an SID", "" }, + { "lsaenumacctwithright",cmd_lsa_enum_acct_with_right,PI_LSARPC,"Enumerate accounts with a right", "" }, + { "lsaaddacctrights", cmd_lsa_add_acct_rights, PI_LSARPC, "Add rights to an account", "" }, + { "lsaremoveacctrights", cmd_lsa_remove_acct_rights, PI_LSARPC, "Remove rights from an account", "" }, + { "lsalookupprivvalue", cmd_lsa_lookupprivvalue, PI_LSARPC, "Get a privilege value given its name", "" }, + { "lsaquerysecobj", cmd_lsa_query_secobj, PI_LSARPC, "Query LSA security object", "" }, + + { NULL } +}; diff --git a/source4/rpcclient/cmd_netlogon.c b/source4/rpcclient/cmd_netlogon.c new file mode 100644 index 0000000000..407bff3735 --- /dev/null +++ b/source4/rpcclient/cmd_netlogon.c @@ -0,0 +1,342 @@ +/* + 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_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; + unsigned char trust_passwd[16]; + uint32 database_id = 0, num_deltas; + SAM_DELTA_HDR *hdr_deltas; + SAM_DELTA_CTR *deltas; + DOM_CRED ret_creds; + uint32 neg_flags = 0x000001ff; + + if (argc > 2) { + fprintf(stderr, "Usage: %s [database_id]\n", argv[0]); + return NT_STATUS_OK; + } + + if (argc == 2) + database_id = atoi(argv[1]); + + if (!secrets_init()) { + fprintf(stderr, "Unable to initialise secrets database\n"); + return result; + } + + /* Initialise session credentials */ + + if (!secrets_fetch_trust_account_password(lp_workgroup(), trust_passwd, + NULL)) { + fprintf(stderr, "could not fetch trust account password\n"); + goto done; + } + + result = cli_nt_setup_creds(cli, get_sec_chan(), trust_passwd, &neg_flags, 2); + + if (!NT_STATUS_IS_OK(result)) { + fprintf(stderr, "Error initialising session creds\n"); + goto done; + } + + /* 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; + unsigned char trust_passwd[16]; + uint32 database_id, num_deltas, tmp; + SAM_DELTA_HDR *hdr_deltas; + SAM_DELTA_CTR *deltas; + UINT64_S seqnum; + uint32 neg_flags = 0x000001ff; + + 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; + + if (!secrets_init()) { + fprintf(stderr, "Unable to initialise secrets database\n"); + goto done; + } + + /* Initialise session credentials */ + + if (!secrets_fetch_trust_account_password(lp_workgroup(), trust_passwd, + NULL)) { + fprintf(stderr, "could not fetch trust account password\n"); + goto done; + } + + result = cli_nt_setup_creds(cli, get_sec_chan(), trust_passwd, &neg_flags, 2); + + if (!NT_STATUS_IS_OK(result)) { + fprintf(stderr, "Error initialising session creds\n"); + goto done; + } + + /* Synchronise sam database */ + + 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) +{ + unsigned char trust_passwd[16]; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + int logon_type = NET_LOGON_TYPE; + const char *username, *password; + uint32 neg_flags = 0x000001ff; + int auth_level = 2; + + /* Check arguments */ + + if (argc < 3 || argc > 6) { + fprintf(stderr, "Usage: samlogon " + "[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); + + /* Authenticate ourselves with the domain controller */ + + if (!secrets_init()) { + fprintf(stderr, "Unable to initialise secrets database\n"); + return result; + } + + if (!secrets_fetch_trust_account_password(lp_workgroup(), trust_passwd, NULL)) { + fprintf(stderr, "could not fetch trust account password\n"); + goto done; + } + + result = cli_nt_setup_creds(cli, get_sec_chan(), trust_passwd, &neg_flags, auth_level); + + if (!NT_STATUS_IS_OK(result)) { + fprintf(stderr, "Error initialising session creds\n"); + goto done; + } + + /* Perform the sam logon */ + + result = cli_netlogon_sam_logon(cli, mem_ctx, username, password, logon_type); + + 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", cmd_netlogon_logon_ctrl2, PI_NETLOGON, "Logon Control 2", "" }, + { "logonctrl", cmd_netlogon_logon_ctrl, PI_NETLOGON, "Logon Control", "" }, + { "samsync", cmd_netlogon_sam_sync, PI_NETLOGON, "Sam Synchronisation", "" }, + { "samdeltas", cmd_netlogon_sam_deltas, PI_NETLOGON, "Query Sam Deltas", "" }, + { "samlogon", cmd_netlogon_sam_logon, PI_NETLOGON, "Sam Logon", "" }, + + { NULL } +}; diff --git a/source4/rpcclient/cmd_reg.c b/source4/rpcclient/cmd_reg.c new file mode 100644 index 0000000000..19c0e7f71f --- /dev/null +++ b/source4/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 \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 \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 \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 \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 \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 \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_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 \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 \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': + safe_strcpy(msg, optarg, sizeof(msg)-1); + /*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", cmd_reg_shutdown, PI_WINREG, "Remote Shutdown", + "syntax: shutdown [-m message] [-t timeout] [-r] [-h] [-f] (-r == reboot, -h == halt, -f == force)" }, + + { "abortshutdown", cmd_reg_abort_shutdown, PI_WINREG, "Abort Shutdown", + "syntax: abortshutdown" }, +/* + { "regenum", cmd_reg_enum, "Registry Enumeration", + "" }, + + { "regdeletekey", cmd_reg_delete_key, "Registry Key Delete", + "" }, + + { "regcreatekey", cmd_reg_create_key, "Registry Key Create", + " [keyclass]" }, + + { "regqueryval", cmd_reg_query_info, "Registry Value Query", + "" }, + + { "regquerykey", cmd_reg_query_key, "Registry Key Query", + "" }, + + { "regdeleteval", cmd_reg_delete_val, "Registry Value Delete", + "" }, + + { "regcreateval", cmd_reg_create_val, "Registry Key Create", + " " }, + + { "reggetsec", cmd_reg_get_key_sec, "Registry Key Security", + "" }, + + { "regtestsec", cmd_reg_test_key_sec, "Test Registry Key Security", + "" }, +*/ + { NULL } +}; diff --git a/source4/rpcclient/cmd_samr.c b/source4/rpcclient/cmd_samr.c new file mode 100644 index 0000000000..cec6b1680b --- /dev/null +++ b/source4/rpcclient/cmd_samr.c @@ -0,0 +1,1517 @@ +/* + 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("\tunknown_3:\t0x%08x\n", usr->unknown_3); /* 0x00ff ffff */ + printf("\tlogon_divs:\t%d\n", usr->logon_divs); /* 0x0000 00a8 which is 168 which is num hrs in a week */ + printf("\tunknown_5:\t0x%08x\n", usr->unknown_5); /* 0x0002 0000 */ + + 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); + + snprintf(string, sizeof(string)-1, "%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; iacct_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; igrp_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 (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 (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 (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 (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 (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("group:[%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 (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; +} + + +/* List of commands exported by this module */ + +struct cmd_set samr_commands[] = { + + { "SAMR" }, + + { "queryuser", cmd_samr_query_user, PI_SAMR, "Query user info", "" }, + { "querygroup", cmd_samr_query_group, PI_SAMR, "Query group info", "" }, + { "queryusergroups", cmd_samr_query_usergroups, PI_SAMR, "Query user groups", "" }, + { "queryuseraliases", cmd_samr_query_useraliases, PI_SAMR, "Query user aliases", "" }, + { "querygroupmem", cmd_samr_query_groupmem, PI_SAMR, "Query group membership", "" }, + { "queryaliasmem", cmd_samr_query_aliasmem, PI_SAMR, "Query alias membership", "" }, + { "querydispinfo", cmd_samr_query_dispinfo, PI_SAMR, "Query display info", "" }, + { "querydominfo", cmd_samr_query_dominfo, PI_SAMR, "Query domain info", "" }, + { "enumdomusers", cmd_samr_enum_dom_users, PI_SAMR, "Enumerate domain users", "" }, + { "enumdomgroups", cmd_samr_enum_dom_groups, PI_SAMR, "Enumerate domain groups", "" }, + { "enumalsgroups", cmd_samr_enum_als_groups, PI_SAMR, "Enumerate alias groups", "" }, + + { "createdomuser", cmd_samr_create_dom_user, PI_SAMR, "Create domain user", "" }, + { "samlookupnames", cmd_samr_lookup_names, PI_SAMR, "Look up names", "" }, + { "samlookuprids", cmd_samr_lookup_rids, PI_SAMR, "Look up names", "" }, + { "deletedomuser", cmd_samr_delete_dom_user, PI_SAMR, "Delete domain user", "" }, + { "samquerysecobj", cmd_samr_query_sec_obj, PI_SAMR, "Query SAMR security object", "" }, + { "getdompwinfo", cmd_samr_get_dom_pwinfo, PI_SAMR, "Retrieve domain password info", "" }, + + { NULL } +}; diff --git a/source4/rpcclient/cmd_spoolss.c b/source4/rpcclient/cmd_spoolss.c new file mode 100644 index 0000000000..9f6f539e19 --- /dev/null +++ b/source4/rpcclient/cmd_spoolss.c @@ -0,0 +1,2297 @@ +/* + 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 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. +****************************************************************************/ +BOOL get_short_archi(char *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 False; + } + + /* this might be client code - but shouldn't this be an fstrcpy etc? */ + + StrnCpy (short_archi, archi_table[i].short_archi, strlen(archi_table[i].short_archi)); + + DEBUGADD(108,("index: [%d]\n", i)); + DEBUGADD(108,("long architecture: [%s]\n", long_archi)); + DEBUGADD(108,("short architecture: [%s]\n", short_archi)); + + return True; +} + +#if 0 +/********************************************************************** + * dummy function -- placeholder + */ +static NTSTATUS 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 NT_STATUS_OK; +} +#endif + +/*********************************************************************** + * Get printer information + */ +static NTSTATUS 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 \n", argv[0]); + return NT_STATUS_OK; + } + + if (!cli) + return NT_STATUS_UNSUCCESSFUL; + + slprintf (servername, sizeof(servername)-1, "\\\\%s", cli->desthost); + strupper (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 W_ERROR_IS_OK(werror) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; +} + + +/**************************************************************************** +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 NTSTATUS 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 NT_STATUS_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(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 W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; +} + +/**************************************************************************** +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[%d]\n", i2->port_type); + printf("\tReserved:\t[%d]\n", i2->reserved); + printf("\n"); +} + +/* Enumerate ports */ + +static NTSTATUS 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 NT_STATUS_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 W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; +} + +/*********************************************************************** + * Set printer comment - use a level2 set. + */ +static NTSTATUS 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 NT_STATUS_OK; + } + + /* Open a printer handle */ + if (argc == 3) { + fstrcpy(comment, argv[2]); + } + + slprintf (servername, sizeof(servername)-1, "\\\\%s", cli->desthost); + strupper (servername); + fstrcpy (printername, 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; + + + /* 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 W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; +} + +/*********************************************************************** + * Get printer information + */ +static NTSTATUS 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 [level]\n", argv[0]); + return NT_STATUS_OK; + } + + /* Open a printer handle */ + if (argc == 3) { + info_level = atoi(argv[2]); + } + + slprintf (servername, sizeof(servername)-1, "\\\\%s", cli->desthost); + strupper (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 W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; +} + +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 NTSTATUS 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 \n", argv[0]); + printf(" of . queries print server\n"); + return NT_STATUS_OK; + } + valuename = argv[2]; + + /* Open a printer handle */ + + slprintf (servername, sizeof(servername)-1, "\\\\%s", cli->desthost); + strupper (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 W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; +} + +/*********************************************************************** + * Get printer data + */ +static NTSTATUS 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 \n", + argv[0]); + printf(" of . queries print server\n"); + return NT_STATUS_OK; + } + valuename = argv[3]; + keyname = argv[2]; + + /* Open a printer handle */ + + slprintf (servername, sizeof(servername)-1, "\\\\%s", cli->desthost); + strupper (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 W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; +} + +/**************************************************************************** +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 NTSTATUS cmd_spoolss_getdriver(struct cli_state *cli, + TALLOC_CTX *mem_ctx, + int argc, const char **argv) +{ + POLICY_HND pol; + WERROR werror; + NTSTATUS result; + uint32 info_level = 3; + BOOL opened_hnd = False; + PRINTER_DRIVER_CTR ctr; + fstring printername, + servername, + user; + uint32 i; + + if ((argc == 1) || (argc > 3)) + { + printf("Usage: %s [level]\n", argv[0]); + return NT_STATUS_OK; + } + + /* get the arguments need to open the printer handle */ + slprintf (servername, sizeof(servername)-1, "\\\\%s", cli->desthost); + strupper (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); + + result = W_ERROR_IS_OK(werror) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; + + if (!NT_STATUS_IS_OK(result)) { + printf("Error opening printer handle for %s!\n", printername); + return result; + } + + 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, &ctr); + + if (W_ERROR_V(werror) == ERRinsufficientbuffer) + werror = cli_spoolss_getprinterdriver( + cli, mem_ctx, needed, NULL, &pol, info_level, + archi_table[i].long_archi, &ctr); + + if (!W_ERROR_IS_OK(werror)) + continue; + + 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); + + return W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; +} + +/*********************************************************************** + * Get printer information + */ +static NTSTATUS 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 NT_STATUS_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 W_ERROR_IS_OK(werror) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; +} + +/**************************************************************************** +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 NTSTATUS 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 NT_STATUS_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 W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; +} + +/******************************************************************************* + 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 (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 + :::\ + :::\ + : + *******************************************************************************/ +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); + + /* */ + 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; idependentfiles[i] = SSVAL(&info->dependentfiles[i], 0, str2[i]); + } + info->dependentfiles[len] = '\0'; + + return True; +} + + +static NTSTATUS 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; + fstring arch; + fstring driver_name; + + /* parse the command arguements */ + if (argc != 3) + { + printf ("Usage: %s \\\n", argv[0]); + printf ("\t:::\\\n"); + printf ("\t:::\\\n"); + printf ("\t:\n"); + + return NT_STATUS_OK; + } + + /* Fill in the DRIVER_INFO_3 struct */ + ZERO_STRUCT(info3); + if (!get_short_archi(arch, argv[1])) + { + printf ("Error Unknown architechture [%s]\n", argv[1]); + return NT_STATUS_INVALID_PARAMETER; + } + 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 NT_STATUS_INVALID_PARAMETER; + } + + + 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 W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; +} + + +static NTSTATUS 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 \n", argv[0]); + return NT_STATUS_OK; + } + + slprintf (servername, sizeof(servername)-1, "\\\\%s", cli->desthost); + strupper (servername); + + /* Fill in the DRIVER_INFO_3 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 W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; +} + +static NTSTATUS 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 \n", argv[0]); + return NT_STATUS_OK; + } + + slprintf (servername, sizeof(servername)-1, "\\\\%s", cli->desthost); + strupper (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 */ + + 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 W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; +} + + +static NTSTATUS 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 \n", argv[0]); + return NT_STATUS_OK; + } + + slprintf (servername, sizeof(servername)-1, "\\\\%s", cli->desthost); + strupper (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 W_ERROR_IS_OK(result) || W_ERROR_EQUAL(result, WERR_UNKNOWN_PRINTER_DRIVER) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; +} + +static NTSTATUS 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 NT_STATUS_OK; + } + + if (asprintf(&servername, "\\\\%s", cli->desthost) < 0) + return NT_STATUS_NO_MEMORY; + strupper(servername); + + if (asprintf(&environment, "%s", (argc == 2) ? argv[1] : + PRINTER_DRIVER_ARCHITECTURE) < 0) { + SAFE_FREE(servername); + return NT_STATUS_NO_MEMORY; + } + + 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 W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; +} + +/* Add a form */ + +static NTSTATUS 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 \n", argv[0]); + return NT_STATUS_OK; + } + + /* Get a printer handle */ + + asprintf(&servername, "\\\\%s", cli->desthost); + strupper(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], strlen(argv[2]) + 1); + + /* 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 W_ERROR_IS_OK(werror) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; +} + +/* Set a form */ + +static NTSTATUS 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 \n", argv[0]); + return NT_STATUS_OK; + } + + /* Get a printer handle */ + + asprintf(&servername, "\\\\%s", cli->desthost); + strupper(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], strlen(argv[2]) + 1); + + /* 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 W_ERROR_IS_OK(werror) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; +} + +/* Get a form */ + +static NTSTATUS 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 \n", argv[0]); + return NT_STATUS_OK; + } + + /* Get a printer handle */ + + asprintf(&servername, "\\\\%s", cli->desthost); + strupper(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 W_ERROR_IS_OK(werror) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; +} + +/* Delete a form */ + +static NTSTATUS 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 \n", argv[0]); + return NT_STATUS_OK; + } + + /* Get a printer handle */ + + asprintf(&servername, "\\\\%s", cli->desthost); + strupper(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 W_ERROR_IS_OK(werror) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; +} + +/* Enumerate forms */ + +static NTSTATUS 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 \n", argv[0]); + return NT_STATUS_OK; + } + + /* Get a printer handle */ + + asprintf(&servername, "\\\\%s", cli->desthost); + strupper(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 W_ERROR_IS_OK(werror) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; +} + +static NTSTATUS 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 \n", argv[0]); + return NT_STATUS_OK; + } + + slprintf (servername, sizeof(servername)-1, "\\\\%s", cli->desthost); + strupper (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 W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; +} + +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 NTSTATUS 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 NT_STATUS_OK; + } + + if (argc == 3) + level = atoi(argv[2]); + + /* Open printer handle */ + + slprintf(servername, sizeof(servername)-1, "\\\\%s", cli->desthost); + strupper(servername); + fstrcpy(user, cli->user_name); + slprintf(printername, sizeof(servername)-1, "\\\\%s\\", cli->desthost); + strupper(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 W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; +} + +/* enumerate data */ + +static NTSTATUS 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 NT_STATUS_OK; + } + + /* Open printer handle */ + + slprintf(servername, sizeof(servername)-1, "\\\\%s", cli->desthost); + strupper(servername); + fstrcpy(user, cli->user_name); + slprintf(printername, sizeof(printername)-1, "\\\\%s\\", cli->desthost); + strupper(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 W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; +} + +/* enumerate data for a given key */ + +static NTSTATUS 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 \n", argv[0]); + return NT_STATUS_OK; + } + + keyname = argv[2]; + + /* Open printer handle */ + + slprintf(servername, sizeof(servername)-1, "\\\\%s", cli->desthost); + strupper(servername); + fstrcpy(user, cli->user_name); + slprintf(printername, sizeof(printername)-1, "\\\\%s\\", cli->desthost); + strupper(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 W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; +} + +/* enumerate subkeys */ + +static NTSTATUS 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 NT_STATUS_OK; + } + + if (argc == 3) + keyname = argv[2]; + else + keyname = ""; + + /* Open printer handle */ + + slprintf(servername, sizeof(servername)-1, "\\\\%s", cli->desthost); + strupper(servername); + fstrcpy(user, cli->user_name); + slprintf(printername, sizeof(printername)-1, "\\\\%s\\", cli->desthost); + strupper(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 W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; +} + +static NTSTATUS 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(servername); + + slprintf(printername, sizeof(printername) - 1, "\\\\%s\\%s", cli->desthost, + argv[1]); + strupper(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(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 W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; +} + +/* List of commands exported by this module */ +struct cmd_set spoolss_commands[] = { + + { "SPOOLSS" }, + + { "adddriver", cmd_spoolss_addprinterdriver, PI_SPOOLSS, "Add a print driver", "" }, + { "addprinter", cmd_spoolss_addprinterex, PI_SPOOLSS, "Add a printer", "" }, + { "deldriver", cmd_spoolss_deletedriver, PI_SPOOLSS, "Delete a printer driver", "" }, + { "enumdata", cmd_spoolss_enum_data, PI_SPOOLSS, "Enumerate printer data", "" }, + { "enumdataex", cmd_spoolss_enum_data_ex, PI_SPOOLSS, "Enumerate printer data for a key", "" }, + { "enumkey", cmd_spoolss_enum_printerkey, PI_SPOOLSS, "Enumerate printer keys", "" }, + { "enumjobs", cmd_spoolss_enum_jobs, PI_SPOOLSS, "Enumerate print jobs", "" }, + { "enumports", cmd_spoolss_enum_ports, PI_SPOOLSS, "Enumerate printer ports", "" }, + { "enumdrivers", cmd_spoolss_enum_drivers, PI_SPOOLSS, "Enumerate installed printer drivers", "" }, + { "enumprinters", cmd_spoolss_enum_printers, PI_SPOOLSS, "Enumerate printers", "" }, + { "getdata", cmd_spoolss_getprinterdata, PI_SPOOLSS, "Get print driver data", "" }, + { "getdataex", cmd_spoolss_getprinterdataex, PI_SPOOLSS, "Get printer driver data with keyname", ""}, + { "getdriver", cmd_spoolss_getdriver, PI_SPOOLSS, "Get print driver information", "" }, + { "getdriverdir", cmd_spoolss_getdriverdir, PI_SPOOLSS, "Get print driver upload directory", "" }, + { "getprinter", cmd_spoolss_getprinter, PI_SPOOLSS, "Get printer info", "" }, + { "getprintprocdir", cmd_spoolss_getprintprocdir, PI_SPOOLSS, "Get print processor directory", "" }, + { "openprinter", cmd_spoolss_open_printer_ex, PI_SPOOLSS, "Open printer handle", "" }, + { "setdriver", cmd_spoolss_setdriver, PI_SPOOLSS, "Set printer driver", "" }, + { "getprintprocdir", cmd_spoolss_getprintprocdir, PI_SPOOLSS, "Get print processor directory", "" }, + { "addform", cmd_spoolss_addform, PI_SPOOLSS, "Add form", "" }, + { "setform", cmd_spoolss_setform, PI_SPOOLSS, "Set form", "" }, + { "getform", cmd_spoolss_getform, PI_SPOOLSS, "Get form", "" }, + { "deleteform", cmd_spoolss_deleteform, PI_SPOOLSS, "Delete form", "" }, + { "enumforms", cmd_spoolss_enum_forms, PI_SPOOLSS, "Enumerate forms", "" }, + { "setprinter", cmd_spoolss_setprinter, PI_SPOOLSS, "Set printer comment", "" }, + { "setprinterdata", cmd_spoolss_setprinterdata, PI_SPOOLSS, "Set REG_SZ printer data", "" }, + { "rffpcnex", cmd_spoolss_rffpcnex, PI_SPOOLSS, "Rffpcnex test", "" }, + + { NULL } +}; diff --git a/source4/rpcclient/cmd_srvsvc.c b/source4/rpcclient/cmd_srvsvc.c new file mode 100644 index 0000000000..8597c7bc2e --- /dev/null +++ b/source4/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 NTSTATUS 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 NT_STATUS_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 W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; +} + +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 NTSTATUS 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 NT_STATUS_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 W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; +} + +static NTSTATUS 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 NT_STATUS_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 W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; +} + +static NTSTATUS 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 NT_STATUS_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 W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; +} + +/* List of commands exported by this module */ + +struct cmd_set srvsvc_commands[] = { + + { "SRVSVC" }, + + { "srvinfo", cmd_srvsvc_srv_query_info, PI_SRVSVC, "Server query info", "" }, + { "netshareenum", cmd_srvsvc_net_share_enum, PI_SRVSVC, "Enumerate shares", "" }, + { "netfileenum", cmd_srvsvc_net_file_enum, PI_SRVSVC, "Enumerate open files", "" }, + { "netremotetod", cmd_srvsvc_net_remote_tod, PI_SRVSVC, "Fetch remote time of day", "" }, + + { NULL } +}; diff --git a/source4/rpcclient/cmd_wkssvc.c b/source4/rpcclient/cmd_wkssvc.c new file mode 100644 index 0000000000..bb118234c0 --- /dev/null +++ b/source4/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(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/source4/rpcclient/display_sec.c b/source4/rpcclient/display_sec.c new file mode 100644 index 0000000000..2a93c915f1 --- /dev/null +++ b/source4/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/source4/rpcclient/rpcclient.c b/source4/rpcclient/rpcclient.c new file mode 100644 index 0000000000..c9d8cd96f3 --- /dev/null +++ b/source4/rpcclient/rpcclient.c @@ -0,0 +1,756 @@ +/* + 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(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].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; +} + +/*********************************************************************** + * read in username/password credentials from a file + */ +static void read_authfile ( + char *filename, + char* username, + char* password, + char* domain +) +{ + FILE *auth; + fstring buf; + uint16 len = 0; + char *ptr, *val, *param; + + if ((auth=sys_fopen(filename, "r")) == NULL) + { + printf ("ERROR: Unable to open credentials file!\n"); + return; + } + + while (!feof(auth)) + { + /* get a line from the file */ + if (!fgets (buf, sizeof(buf), auth)) + continue; + + len = strlen(buf); + + /* skip empty lines */ + 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) + fstrcpy (password, val); + else if (strwicmp("username", param) == 0) + fstrcpy (username, val); + else if (strwicmp("domain", param) == 0) + fstrcpy (domain, val); + + memset(buf, 0, sizeof(buf)); + } + fclose(auth); + + return; +} + +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; +} + + +/** + * Find default username from environment variables. + * + * @param username fstring to receive username; not touched if none is + * known. + **/ +static void get_username (char *username) +{ + if (getenv("USER")) + fstrcpy(username,getenv("USER")); + + if (*username == 0 && getenv("LOGNAME")) + fstrcpy(username,getenv("LOGNAME")); + + if (*username == 0) { + fstrcpy(username,"GUEST"); + } + + return; +} + +/* 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; + fstring domain_name; + static BOOL got_domain_sid; + TALLOC_CTX *mem_ctx; + + 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, &domain_sid); + if (!NT_STATUS_IS_OK(result)) { + goto error; + } + + got_domain_sid = True; + + 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 \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 */ +} + +/* Built in rpcclient commands */ + +static struct cmd_set rpcclient_commands[] = { + + { "GENERAL OPTIONS" }, + + { "help", cmd_help, -1, "Get help on commands", "[command]" }, + { "?", cmd_help, -1, "Get help on commands", "[command]" }, + { "debuglevel", cmd_debuglevel, -1, "Set debug level", "level" }, + { "list", cmd_listcommands, -1, "List available commands on ", "pipe" }, + { "exit", cmd_quit, -1, "Exit program", "" }, + { "quit", cmd_quit, -1, "Exit program", "" }, + + { NULL } +}; + +static struct cmd_set separator_command[] = { + { "---------------", 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[]; + +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, + 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 result; + + TALLOC_CTX *mem_ctx; + + /* Create mem_ctx */ + + if (!(mem_ctx = talloc_init("do_cmd"))) { + DEBUG(0, ("talloc_init() failed\n")); + return NT_STATUS_UNSUCCESSFUL; + } + + /* Open pipe */ + + if (cmd_entry->pipe_idx != -1) + if (!cli_nt_session_open(cli, cmd_entry->pipe_idx)) { + DEBUG(0, ("Could not initialize pipe\n")); + return NT_STATUS_UNSUCCESSFUL; + } + + /* Run command */ + + result = cmd_entry->fn(cli, mem_ctx, argc, (const char **) argv); + + /* Cleanup */ + + if (cmd_entry->pipe_idx != -1) + cli_nt_session_close(cli); + + talloc_destroy(mem_ctx); + + return result; +} + + +/** + * 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->fn) { + 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: + 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[]) +{ + static int got_pass = 0; + BOOL interactive = True; + int opt; + static char *cmdstr = ""; + const char *server; + struct cli_state *cli; + fstring password="", + username="", + domain=""; + static char *opt_authfile=NULL, + *opt_username=NULL, + *opt_domain=NULL, + *opt_logfile=NULL, + *opt_ipaddr=NULL; + pstring logfile; + 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 + {"authfile", 'A', POPT_ARG_STRING, &opt_authfile, 'A', "File containing user credentials", "AUTHFILE"}, + {"nopass", 'N', POPT_ARG_NONE, &got_pass, 'N', "Don't ask for a password"}, + {"user", 'U', POPT_ARG_STRING, &opt_username, 'U', "Set the network username", "USER"}, + {"workgroup", 'W', POPT_ARG_STRING, &opt_domain, 'W', "Set the domain name for user account", "DOMAIN"}, + {"command", 'c', POPT_ARG_STRING, &cmdstr, 'c', "Execute semicolon separated cmds", "COMMANDS"}, + {"logfile", 'l', POPT_ARG_STRING, &opt_logfile, 'l', "Logfile to use instead of stdout", "LOGFILE" }, + {"dest-ip", 'I', POPT_ARG_STRING, &opt_ipaddr, 'I', "Specify destination IP address", "IP"}, + { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_debug }, + { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_configfile }, + { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_version}, + { NULL } + }; + + setlinebuf(stdout); + + /* 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 'A': + /* only get the username, password, and domain from the file */ + read_authfile (opt_authfile, username, password, domain); + if (strlen (password)) + got_pass = 1; + break; + + case 'l': + slprintf(logfile, sizeof(logfile) - 1, "%s.client", + opt_logfile); + lp_set_logfile(logfile); + interactive = False; + break; + + case 'U': { + char *lp; + + fstrcpy(username,opt_username); + + if ((lp=strchr_m(username,'%'))) { + *lp = 0; + fstrcpy(password,lp+1); + got_pass = 1; + memset(strchr_m(opt_username,'%') + 1, 'X', + strlen(password)); + } + break; + } + 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; + } + case 'W': + fstrcpy(domain, opt_domain); + break; + } + } + + /* 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); + + /* 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); + + load_interfaces(); + + if (!init_names()) + return 1; + + /* Resolve the IP address */ + + if (!opt_ipaddr && !resolve_name(server, &server_ip, 0x20)) { + fprintf(stderr, "Unable to resolve %s\n", server); + return 1; + } + + /* + * Get password + * from stdin if necessary + */ + + if (!got_pass) { + char *pass = getpass("Password:"); + if (pass) { + fstrcpy(password, pass); + } + } + + if (!strlen(username) && !got_pass) + get_username(username); + + nt_status = cli_full_connection(&cli, lp_netbios_name(), server, + &server_ip, 0, + "IPC$", "IPC", + username, domain, + password, 0, 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(password,'X',sizeof(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[0]) { + char *cmd; + char *p = cmdstr; + + while((cmd=next_command(&p)) != NULL) { + process_cmd(cli, cmd); + } + + cli_shutdown(cli); + return 0; + } + + /* 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/source4/rpcclient/rpcclient.h b/source4/rpcclient/rpcclient.h new file mode 100644 index 0000000000..1bd3c1a641 --- /dev/null +++ b/source4/rpcclient/rpcclient.h @@ -0,0 +1,34 @@ +/* + 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 + +struct cmd_set { + const char *name; + NTSTATUS (*fn)(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/source4/sam/SAM-interface_handles.txt b/source4/sam/SAM-interface_handles.txt new file mode 100644 index 0000000000..1c164bd198 --- /dev/null +++ b/source4/sam/SAM-interface_handles.txt @@ -0,0 +1,123 @@ +SAM API + +NTSTATUS sam_get_sec_obj(NT_USER_TOKEN *access, DOM_SID *sid, SEC_DESC **sd) +NTSTATUS sam_set_sec_obj(NT_USER_TOKEN *access, DOM_SID *sid, SEC_DESC *sd) + +NTSTATUS sam_lookup_name(NT_USER_TOKEN *access, DOM_SID *domain, char *name, DOM_SID **sid, uint32 *type) +NTSTATUS sam_lookup_sid(NT_USER_TOKEN *access, DOM_SID *sid, char **name, uint32 *type) + + +Domain API + +NTSTATUS sam_update_domain(SAM_DOMAIN_HANDLE *domain) + +NTSTATUS sam_enum_domains(NT_USER_TOKEN *access, int32 *domain_count, DOM_SID **domains, char **domain_names) +NTSTATUS sam_lookup_domain(NT_USER_TOKEN *access, char *domain, DOM_SID **domainsid) + +NTSTATUS sam_get_domain_by_sid(NT_USER_TOKEN *access, uint32 access_desired, DOM_SID *domainsid, SAM_DOMAIN_HANDLE **domain) + + +User API + +NTSTATUS sam_create_user(NT_USER_TOKEN *access, uint32 access_desired, SAM_USER_HANDLE **user) +NTSTATUS sam_add_user(SAM_USER_HANDLE *user) +NTSTATUS sam_update_user(SAM_USER_HANDLE *user) +NTSTATUS sam_delete_user(SAM_USER_HANDLE * user) + +NTSTATUS sam_enum_users(NT_USER_TOKEN *access, DOM_SID *domain, int32 *user_count, SAM_USER_ENUM **users) + +NTSTATUS sam_get_user_by_sid(NT_USER_TOKEN *access, uint32 access_desired, DOM_SID *usersid, SAM_USER_HANDLE **user) +NTSTATUS sam_get_user_by_name(NT_USER_TOKEN *access, uint32 access_desired, char *domain, char *name, SAM_USER_HANDLE **user) + + +Group API + +NTSTATUS sam_create_group(NT_USER_TOKEN *access, uint32 access_desired, uint32 typ, SAM_GROUP_HANDLE **group) +NTSTATUS sam_add_group(SAM_GROUP_HANDLE *samgroup) +NTSTATUS sam_update_group(SAM_GROUP_HANDLE *samgroup) +NTSTATUS sam_delete_group(SAM_GROUP_HANDLE *groupsid) + +NTSTATUS sam_enum_groups(NT_USER_TOKEN *access, DOM_SID *domainsid, uint32 typ, uint32 *groups_count, SAM_GROUP_ENUM **groups) + +NTSTATUS sam_get_group_by_sid(NT_USER_TOKEN *access, uint32 access_desired, DOM_SID *groupsid, SAM_GROUP_HANDLE **group) +NTSTATUS sam_get_group_by_name(NT_USER_TOKEN *access, uint32 access_desired, char *domain, char *name, SAM_GROUP_HANDLE **group) + +NTSTATUS sam_add_member_to_group(SAM_GROUP_HANDLE *group, SAM_GROUP_MEMBER *member) +NTSTATUS sam_delete_member_from_group(SAM_GROUP_HANDLE *group, SAM_GROUP_MEMBER *member) +NTSTATUS sam_enum_groupmembers(SAM_GROUP_HANLDE *group, uint32 *members_count, SAM_GROUP_MEMBER **members) + +NTSTATUS sam_get_groups_of_user(SAM_USER_HANDLE *user, uint32 typ, uint32 *group_count, SAM_GROUP_ENUM **groups) + + + +structures + +typedef _SAM_GROUP_MEMBER { + DOM_SID sid; + BOOL group; /* specifies if it is a group or a user */ + +} SAM_GROUP_MEMBER + +typedef struct sam_user_enum { + DOM_SID sid; + char *username; + char *full_name; + char *user_desc; + uint16 acc_ctrl; +} SAM_USER_ENUM; + +typedef struct sam_group_enum { + DOM_SID sid; + char *groupname; + char *comment; +} SAM_GROUP_ENUM + +NTSTATUS sam_get_domain_sid(SAM_DOMAIN_HANDLE *domain, DOM_SID **sid) +NTSTATUS sam_get_domain_num_users(SAM_DOMAIN_HANDLE *domain, uint32 *num_users) +NTSTATUS sam_get_domain_num_groups(SAM_DOMAIN_HANDLE *domain, uint32 *num_groups) +NTSTATUS sam_get_domain_num_aliases(SAM_DOMAIN_HANDLE *domain, uint32 *num_aliases) +NTSTATUS sam_{get,set}_domain_name(SAM_DOMAIN_HANDLE *domain, char **domain_name) +NTSTATUS sam_{get,set}_domain_server(SAM_DOMAIN_HANDLE *domain, char **server_name) +NTSTATUS sam_{get,set}_domain_max_pwdage(SAM_DOMAIN_HANDLE *domain, NTTIME *max_passwordage) +NTSTATUS sam_{get,set}_domain_min_pwdage(SAM_DOMAIN_HANDLE *domain, NTTIME *min_passwordage) +NTSTATUS sam_{get,set}_domain_lockout_duration(SAM_DOMAIN_HANDLE *domain, NTTIME *lockout_duration) +NTSTATUS sam_{get,set}_domain_reset_count(SAM_DOMAIN_HANDLE *domain, NTTIME *reset_lockout_count) +NTSTATUS sam_{get,set}_domain_min_pwdlength(SAM_DOMAIN_HANDLE *domain, uint16 *min_passwordlength) +NTSTATUS sam_{get,set}_domain_pwd_history(SAM_DOMAIN_HANDLE *domain, uin16 *password_history) +NTSTATUS sam_{get,set}_domain_lockout_count(SAM_DOMAIN_HANDLE *domain, uint16 *lockout_count) +NTSTATUS sam_{get,set}_domain_force_logoff(SAM_DOMAIN_HANDLE *domain, BOOL *force_logoff) +NTSTATUS sam_{get,set}_domain_login_pwdchange(SAM_DOMAIN_HANDLE *domain, BOOL *login_pwdchange) + +NTSTATUS sam_get_user_sid(SAM_USER_HANDLE *user, DOM_SID **sid) +NTSTATUS sam_{get,set}_user_pgroup(SAM_USER_HANDLE *user, DOM_SID **pgroup) +NTSTATUS sam_{get,set}_user_name(SAM_USER_HANDLE *user, char **username) +NTSTATUS sam_{get,set}_user_fullname(SAM_USER_HANDLE *user, char** fullname) +NTSTATUS sam_{get,set}_user_description(SAM_USER_HANDLE *user, char **description) +NTSTATUS sam_{get,set}_user_home_dir(SAM_USER_HANDLE *user, char **home_dir) +NTSTATUS sam_{get,set}_user_dir_drive(SAM_USER_HANDLE *user, char **dir_drive) +NTSTATUS sam_{get,set}_user_logon_script(SAM_USER_HANDLE *user, char **logon_script) +NTSTATUS sam_{get,set}_user_profile_path(SAM_USER_HANDLE *user, char **profile_path) +NTSTATUS sam_{get,set}_user_workstations(SAM_USER_HANDLE *user, char **workstations) +NTSTATUS sam_{get,set}_user_munged_dial(SAM_USER_HANDLE *user, char **munged_dial) +NTSTATUS sam_{get,set}_user_lm_pwd(SAM_USER_HANDLE *user, DATA_BLOB *lm_pwd) +NTSTATUS sam_{get,set}_user_nt_pwd(SAM_USER_HANDLE *user, DATA_BLOB *nt_pwd) +NTSTATUS sam_{get,set}_user_plain_pwd(SAM_USER_HANDLE *user, DATA_BLOB *plaintext_pwd) +NTSTATUS sam_{get,set}_user_acct_ctrl(SAM_USER_HANDLE *user, uint16 *acct_ctrl) +NTSTATUS sam_{get,set}_user_logon_divs(SAM_USER_HANDLE *user, uint16 *logon_divs) +NTSTATUS sam_{get,set}_user_hours(SAM_USER_HANDLE *user, uint32 *hours_len, uint8 **hours) +NTSTATUS sam_{get,set}_user_logon_time(SAM_USER_HANDLE *user, NTTIME *logon_time) +NTSTATUS sam_{get,set}_user_logoff_time(SAM_USER_HANDLE *user, NTTIME *logoff_time) +NTSTATUS sam_{get,set}_user_kickoff_time(SAM_USER_HANDLE *user, NTTIME kickoff_time) +NTSTATUS sam_{get,set}_user_pwd_last_set(SAM_USER_HANDLE *user, NTTIME pwd_last_set) +NTSTATUS sam_{get,set}_user_pwd_can_change(SAM_USER_HANDLE *user, NTTIME pwd_can_change) +NTSTATUS sam_{get,set}_user_pwd_must_change(SAM_USER_HANDLE *user, NTTIME pwd_must_change) +NTSTATUS sam_{get,set}_user_unknown_1(SAM_USER_HANDLE *user, char **unknown_1) +NTSTATUS sam_{get,set}_user_unknown_2(SAM_USER_HANDLE *user, uint32 *unknown_2) +NTSTATUS sam_{get,set}_user_unknown_3(SAM_USER_HANDLE *user, uint32 *unknown_3) +NTSTATUS sam_{get,set}_user_unknown_4(SAM_USER_HANDLE *user, uint32 *unknown_4) + +NTSTATUS sam_get_group_sid(SAM_GROUP_HANDLE *group, DOM_SID **sid) +NTSTATUS sam_get_group_typ(SAM_GROUP_HANDLE *group, uint32 *typ) +NTSTATUS sam_{get,set}_group_name(SAM_GROUP_HANDLE *group, char **group_name) +NTSTATUS sam_{get,set}_group_comment(SAM_GROUP_HANDLE *group, char **comment) +NTSTATUS sam_{get,set}_group_priv_set(SAM_GROUP_HANDLE *group, PRIVILEGE_SET *priv_set) \ No newline at end of file diff --git a/source4/sam/account.c b/source4/sam/account.c new file mode 100644 index 0000000000..b8336146cd --- /dev/null +++ b/source4/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/source4/sam/get_set_account.c b/source4/sam/get_set_account.c new file mode 100644 index 0000000000..acac281d21 --- /dev/null +++ b/source4/sam/get_set_account.c @@ -0,0 +1,845 @@ +/* + Unix SMB/CIFS implementation. + SAM_ACCOUNT_HANDLE access routines + Copyright (C) Andrew Bartlett 2002 + Copyright (C) Stefan (metze) Metzmacher 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_SAM + +NTSTATUS sam_get_account_domain_sid(const SAM_ACCOUNT_HANDLE *sampass, const DOM_SID **sid) +{ + NTSTATUS status; + SAM_DOMAIN_HANDLE *domain; + SAM_ASSERT(!sampass || !sid); + + if (!NT_STATUS_IS_OK(status = sam_get_account_domain(sampass, &domain))){ + DEBUG(0, ("sam_get_account_domain_sid: Can't get domain for account\n")); + return status; + } + + return sam_get_domain_sid(domain, sid); +} + +NTSTATUS sam_get_account_domain_name(const SAM_ACCOUNT_HANDLE *sampass, const char **domain_name) +{ + NTSTATUS status; + SAM_DOMAIN_HANDLE *domain; + SAM_ASSERT(sampass && domain_name); + + if (!NT_STATUS_IS_OK(status = sam_get_account_domain(sampass, &domain))){ + DEBUG(0, ("sam_get_account_domain_name: Can't get domain for account\n")); + return status; + } + + return sam_get_domain_name(domain, domain_name); +} + +NTSTATUS sam_get_account_acct_ctrl(const SAM_ACCOUNT_HANDLE *sampass, uint16 *acct_ctrl) +{ + SAM_ASSERT(sampass && acct_ctrl); + + *acct_ctrl = sampass->private.acct_ctrl; + + return NT_STATUS_OK; +} + +NTSTATUS sam_get_account_logon_time(const SAM_ACCOUNT_HANDLE *sampass, NTTIME *logon_time) +{ + SAM_ASSERT(sampass && logon_time) ; + + *logon_time = sampass->private.logon_time; + + return NT_STATUS_OK; +} + +NTSTATUS sam_get_account_logoff_time(const SAM_ACCOUNT_HANDLE *sampass, NTTIME *logoff_time) +{ + SAM_ASSERT(sampass && logoff_time) ; + + *logoff_time = sampass->private.logoff_time; + + return NT_STATUS_OK; +} + +NTSTATUS sam_get_account_kickoff_time(const SAM_ACCOUNT_HANDLE *sampass, NTTIME *kickoff_time) +{ + SAM_ASSERT(sampass && kickoff_time); + + *kickoff_time = sampass->private.kickoff_time; + + return NT_STATUS_OK; +} + +NTSTATUS sam_get_account_pass_last_set_time(const SAM_ACCOUNT_HANDLE *sampass, NTTIME *pass_last_set_time) +{ + SAM_ASSERT(sampass && pass_last_set_time); + + *pass_last_set_time = sampass->private.pass_last_set_time; + + return NT_STATUS_OK; +} + +NTSTATUS sam_get_account_pass_can_change_time(const SAM_ACCOUNT_HANDLE *sampass, NTTIME *pass_can_change_time) +{ + SAM_ASSERT(sampass && pass_can_change_time); + + *pass_can_change_time = sampass->private.pass_can_change_time; + + return NT_STATUS_OK; +} + +NTSTATUS sam_get_account_pass_must_change_time(const SAM_ACCOUNT_HANDLE *sampass, NTTIME *pass_must_change_time) +{ + SAM_ASSERT(sampass && pass_must_change_time); + + *pass_must_change_time = sampass->private.pass_must_change_time; + + return NT_STATUS_OK; +} + +NTSTATUS sam_get_account_logon_divs(const SAM_ACCOUNT_HANDLE *sampass, uint16 *logon_divs) +{ + SAM_ASSERT(sampass && logon_divs); + + *logon_divs = sampass->private.logon_divs; + + return NT_STATUS_OK; +} + +NTSTATUS sam_get_account_hours_len(const SAM_ACCOUNT_HANDLE *sampass, uint32 *hours_len) +{ + SAM_ASSERT(sampass && hours_len); + + *hours_len = sampass->private.hours_len; + + return NT_STATUS_OK; +} + +NTSTATUS sam_get_account_hours(const SAM_ACCOUNT_HANDLE *sampass, const uint8 **hours) +{ + SAM_ASSERT(sampass && hours); + + *hours = sampass->private.hours; + + return NT_STATUS_OK; +} + +NTSTATUS sam_get_account_nt_pwd(const SAM_ACCOUNT_HANDLE *sampass, DATA_BLOB *nt_pwd) +{ + SAM_ASSERT(sampass); + + SMB_ASSERT((!sampass->private.nt_pw.data) + || sampass->private.nt_pw.length == NT_HASH_LEN); + + *nt_pwd = sampass->private.nt_pw; + + return NT_STATUS_OK; +} + +NTSTATUS sam_get_account_lm_pwd(const SAM_ACCOUNT_HANDLE *sampass, DATA_BLOB *lm_pwd) +{ + SAM_ASSERT(sampass); + + SMB_ASSERT((!sampass->private.lm_pw.data) + || sampass->private.lm_pw.length == LM_HASH_LEN); + + *lm_pwd = sampass->private.lm_pw; + + return NT_STATUS_OK; +} + +/* 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 sam backends that might + want to store more than just the NTLM hashes. +*/ + +NTSTATUS sam_get_account_plaintext_pwd(const SAM_ACCOUNT_HANDLE *sampass, char **plain_pwd) +{ + SAM_ASSERT(sampass && plain_pwd); + + *plain_pwd = sampass->private.plaintext_pw; + + return NT_STATUS_OK; +} + +NTSTATUS sam_get_account_sid(const SAM_ACCOUNT_HANDLE *sampass, const DOM_SID **sid) +{ + SAM_ASSERT(sampass); + + *sid = &(sampass->private.account_sid); + + return NT_STATUS_OK; +} + +NTSTATUS sam_get_account_pgroup(const SAM_ACCOUNT_HANDLE *sampass, const DOM_SID **sid) +{ + SAM_ASSERT(sampass); + + *sid = &(sampass->private.group_sid); + + return NT_STATUS_OK; +} + +/** + * Get flags showing what is initalised in the SAM_ACCOUNT_HANDLE + * @param sampass the SAM_ACCOUNT_HANDLE in question + * @return the flags indicating the members initialised in the struct. + **/ + +NTSTATUS sam_get_account_init_flag(const SAM_ACCOUNT_HANDLE *sampass, uint32 *initflag) +{ + SAM_ASSERT(sampass); + + *initflag = sampass->private.init_flag; + + return NT_STATUS_OK; +} + +NTSTATUS sam_get_account_name(const SAM_ACCOUNT_HANDLE *sampass, char **account_name) +{ + SAM_ASSERT(sampass); + + *account_name = sampass->private.account_name; + + return NT_STATUS_OK; +} + +NTSTATUS sam_get_account_domain(const SAM_ACCOUNT_HANDLE *sampass, SAM_DOMAIN_HANDLE **domain) +{ + SAM_ASSERT(sampass); + + *domain = sampass->private.domain; + + return NT_STATUS_OK; +} + +NTSTATUS sam_get_account_fullname(const SAM_ACCOUNT_HANDLE *sampass, char **fullname) +{ + SAM_ASSERT(sampass); + + *fullname = sampass->private.full_name; + + return NT_STATUS_OK; +} + +NTSTATUS sam_get_account_homedir(const SAM_ACCOUNT_HANDLE *sampass, char **homedir) +{ + SAM_ASSERT(sampass); + + *homedir = sampass->private.home_dir; + + return NT_STATUS_OK; +} + +NTSTATUS sam_get_account_unix_home_dir(const SAM_ACCOUNT_HANDLE *sampass, char **uhomedir) +{ + SAM_ASSERT(sampass); + + *uhomedir = sampass->private.unix_home_dir; + + return NT_STATUS_OK; +} + +NTSTATUS sam_get_account_dir_drive(const SAM_ACCOUNT_HANDLE *sampass, char **dirdrive) +{ + SAM_ASSERT(sampass); + + *dirdrive = sampass->private.dir_drive; + + return NT_STATUS_OK; +} + +NTSTATUS sam_get_account_logon_script(const SAM_ACCOUNT_HANDLE *sampass, char **logon_script) +{ + SAM_ASSERT(sampass); + + *logon_script = sampass->private.logon_script; + + return NT_STATUS_OK; +} + +NTSTATUS sam_get_account_profile_path(const SAM_ACCOUNT_HANDLE *sampass, char **profile_path) +{ + SAM_ASSERT(sampass); + + *profile_path = sampass->private.profile_path; + + return NT_STATUS_OK; +} + +NTSTATUS sam_get_account_description(const SAM_ACCOUNT_HANDLE *sampass, char **description) +{ + SAM_ASSERT(sampass); + + *description = sampass->private.acct_desc; + + return NT_STATUS_OK; +} + +NTSTATUS sam_get_account_workstations(const SAM_ACCOUNT_HANDLE *sampass, char **workstations) +{ + SAM_ASSERT(sampass); + + *workstations = sampass->private.workstations; + + return NT_STATUS_OK; +} + +NTSTATUS sam_get_account_unknown_str(const SAM_ACCOUNT_HANDLE *sampass, char **unknown_str) +{ + SAM_ASSERT(sampass); + + *unknown_str = sampass->private.unknown_str; + + return NT_STATUS_OK; +} + +NTSTATUS sam_get_account_munged_dial(const SAM_ACCOUNT_HANDLE *sampass, char **munged_dial) +{ + SAM_ASSERT(sampass); + + *munged_dial = sampass->private.munged_dial; + + return NT_STATUS_OK; +} + +NTSTATUS sam_get_account_unknown_1(const SAM_ACCOUNT_HANDLE *sampass, uint32 *unknown1) +{ + SAM_ASSERT(sampass && unknown1); + + *unknown1 = sampass->private.unknown_1; + + return NT_STATUS_OK; +} + +NTSTATUS sam_get_account_unknown_2(const SAM_ACCOUNT_HANDLE *sampass, uint32 *unknown2) +{ + SAM_ASSERT(sampass && unknown2); + + *unknown2 = sampass->private.unknown_2; + + return NT_STATUS_OK; +} + +NTSTATUS sam_get_account_unknown_3(const SAM_ACCOUNT_HANDLE *sampass, uint32 *unknown3) +{ + SAM_ASSERT(sampass && unknown3); + + *unknown3 = sampass->private.unknown_3; + + return NT_STATUS_OK; +} + +/********************************************************************* + Collection of set...() functions for SAM_ACCOUNT_HANDLE_INFO. + ********************************************************************/ + +NTSTATUS sam_set_account_acct_ctrl(SAM_ACCOUNT_HANDLE *sampass, uint16 acct_ctrl) +{ + SAM_ASSERT(sampass); + + sampass->private.acct_ctrl = acct_ctrl; + + return NT_STATUS_OK; +} + +NTSTATUS sam_set_account_logon_time(SAM_ACCOUNT_HANDLE *sampass, NTTIME mytime, BOOL store) +{ + SAM_ASSERT(sampass); + + sampass->private.logon_time = mytime; + + + return NT_STATUS_UNSUCCESSFUL; +} + +NTSTATUS sam_set_account_logoff_time(SAM_ACCOUNT_HANDLE *sampass, NTTIME mytime, BOOL store) +{ + SAM_ASSERT(sampass); + + sampass->private.logoff_time = mytime; + + return NT_STATUS_OK; +} + +NTSTATUS sam_set_account_kickoff_time(SAM_ACCOUNT_HANDLE *sampass, NTTIME mytime, BOOL store) +{ + SAM_ASSERT(sampass); + + sampass->private.kickoff_time = mytime; + + + return NT_STATUS_OK; +} + +NTSTATUS sam_set_account_pass_can_change_time(SAM_ACCOUNT_HANDLE *sampass, NTTIME mytime, BOOL store) +{ + SAM_ASSERT(sampass); + + sampass->private.pass_can_change_time = mytime; + + + return NT_STATUS_OK; +} + +NTSTATUS sam_set_account_pass_must_change_time(SAM_ACCOUNT_HANDLE *sampass, NTTIME mytime, BOOL store) +{ + SAM_ASSERT(sampass); + + sampass->private.pass_must_change_time = mytime; + + return NT_STATUS_OK; +} + +NTSTATUS sam_set_account_pass_last_set_time(SAM_ACCOUNT_HANDLE *sampass, NTTIME mytime) +{ + SAM_ASSERT(sampass); + + sampass->private.pass_last_set_time = mytime; + + return NT_STATUS_OK; +} + +NTSTATUS sam_set_account_hours_len(SAM_ACCOUNT_HANDLE *sampass, uint32 len) +{ + SAM_ASSERT(sampass); + + sampass->private.hours_len = len; + return NT_STATUS_OK; +} + +NTSTATUS sam_set_account_logon_divs(SAM_ACCOUNT_HANDLE *sampass, uint16 hours) +{ + SAM_ASSERT(sampass); + + sampass->private.logon_divs = hours; + return NT_STATUS_OK; +} + +/** + * Set flags showing what is initalised in the SAM_ACCOUNT_HANDLE + * @param sampass the SAM_ACCOUNT_HANDLE in question + * @param flag The *new* flag to be set. Old flags preserved + * this flag is only added. + **/ + +NTSTATUS sam_set_account_init_flag(SAM_ACCOUNT_HANDLE *sampass, uint32 flag) +{ + SAM_ASSERT(sampass); + + sampass->private.init_flag |= flag; + + return NT_STATUS_OK; +} + +NTSTATUS sam_set_account_sid(SAM_ACCOUNT_HANDLE *sampass, const DOM_SID *u_sid) +{ + SAM_ASSERT(sampass && u_sid); + + sid_copy(&sampass->private.account_sid, u_sid); + + DEBUG(10, ("sam_set_account_sid: setting account sid %s\n", + sid_string_static(&sampass->private.account_sid))); + + return NT_STATUS_OK; +} + +NTSTATUS sam_set_account_sid_from_string(SAM_ACCOUNT_HANDLE *sampass, const char *u_sid) +{ + DOM_SID new_sid; + SAM_ASSERT(sampass && u_sid); + + DEBUG(10, ("sam_set_account_sid_from_string: setting account sid %s\n", + u_sid)); + + if (!string_to_sid(&new_sid, u_sid)) { + DEBUG(1, ("sam_set_account_sid_from_string: %s isn't a valid SID!\n", u_sid)); + return NT_STATUS_UNSUCCESSFUL; + } + + if (!NT_STATUS_IS_OK(sam_set_account_sid(sampass, &new_sid))) { + DEBUG(1, ("sam_set_account_sid_from_string: could not set sid %s on SAM_ACCOUNT_HANDLE!\n", u_sid)); + return NT_STATUS_UNSUCCESSFUL; + } + + return NT_STATUS_OK; +} + +NTSTATUS sam_set_account_pgroup_sid(SAM_ACCOUNT_HANDLE *sampass, const DOM_SID *g_sid) +{ + SAM_ASSERT(sampass && g_sid); + + sid_copy(&sampass->private.group_sid, g_sid); + + DEBUG(10, ("sam_set_group_sid: setting group sid %s\n", + sid_string_static(&sampass->private.group_sid))); + + return NT_STATUS_OK; +} + +NTSTATUS sam_set_account_pgroup_string(SAM_ACCOUNT_HANDLE *sampass, const char *g_sid) +{ + DOM_SID new_sid; + SAM_ASSERT(sampass && g_sid); + + DEBUG(10, ("sam_set_group_sid_from_string: setting group sid %s\n", + g_sid)); + + if (!string_to_sid(&new_sid, g_sid)) { + DEBUG(1, ("sam_set_group_sid_from_string: %s isn't a valid SID!\n", g_sid)); + return NT_STATUS_UNSUCCESSFUL; + } + + if (!NT_STATUS_IS_OK(sam_set_account_pgroup_sid(sampass, &new_sid))) { + DEBUG(1, ("sam_set_group_sid_from_string: could not set sid %s on SAM_ACCOUNT_HANDLE!\n", g_sid)); + return NT_STATUS_UNSUCCESSFUL; + } + return NT_STATUS_OK; +} + +/********************************************************************* + Set the domain name. + ********************************************************************/ + +NTSTATUS sam_set_account_domain(SAM_ACCOUNT_HANDLE *sampass, SAM_DOMAIN_HANDLE *domain) +{ + SAM_ASSERT(sampass); + + sampass->private.domain = domain; + + return NT_STATUS_OK; +} + +/********************************************************************* + Set the account's NT name. + ********************************************************************/ + +NTSTATUS sam_set_account_name(SAM_ACCOUNT_HANDLE *sampass, const char *account_name) +{ + SAM_ASSERT(sampass); + + DEBUG(10, ("sam_set_account_name: setting nt account_name %s, was %s\n", account_name, sampass->private.account_name)); + + sampass->private.account_name = talloc_strdup(sampass->mem_ctx, account_name); + + return NT_STATUS_OK; +} + +/********************************************************************* + Set the account's full name. + ********************************************************************/ + +NTSTATUS sam_set_account_fullname(SAM_ACCOUNT_HANDLE *sampass, const char *full_name) +{ + SAM_ASSERT(sampass); + + DEBUG(10, ("sam_set_account_fullname: setting full name %s, was %s\n", full_name, sampass->private.full_name)); + + sampass->private.full_name = talloc_strdup(sampass->mem_ctx, full_name); + + return NT_STATUS_OK; +} + +/********************************************************************* + Set the account's logon script. + ********************************************************************/ + +NTSTATUS sam_set_account_logon_script(SAM_ACCOUNT_HANDLE *sampass, const char *logon_script, BOOL store) +{ + SAM_ASSERT(sampass); + + DEBUG(10, ("sam_set_logon_script: from %s to %s\n", logon_script, sampass->private.logon_script)); + + sampass->private.logon_script = talloc_strdup(sampass->mem_ctx, logon_script); + + + return NT_STATUS_OK; +} + +/********************************************************************* + Set the account's profile path. + ********************************************************************/ + +NTSTATUS sam_set_account_profile_path(SAM_ACCOUNT_HANDLE *sampass, const char *profile_path, BOOL store) +{ + SAM_ASSERT(sampass); + + DEBUG(10, ("sam_set_profile_path: setting profile path %s, was %s\n", profile_path, sampass->private.profile_path)); + + sampass->private.profile_path = talloc_strdup(sampass->mem_ctx, profile_path); + + return NT_STATUS_OK; +} + +/********************************************************************* + Set the account's directory drive. + ********************************************************************/ + +NTSTATUS sam_set_account_dir_drive(SAM_ACCOUNT_HANDLE *sampass, const char *dir_drive, BOOL store) +{ + SAM_ASSERT(sampass); + + DEBUG(10, ("sam_set_dir_drive: setting dir drive %s, was %s\n", dir_drive, + sampass->private.dir_drive)); + + sampass->private.dir_drive = talloc_strdup(sampass->mem_ctx, dir_drive); + + return NT_STATUS_OK; +} + +/********************************************************************* + Set the account's home directory. + ********************************************************************/ + +NTSTATUS sam_set_account_homedir(SAM_ACCOUNT_HANDLE *sampass, const char *home_dir, BOOL store) +{ + SAM_ASSERT(sampass); + + DEBUG(10, ("sam_set_homedir: setting home dir %s, was %s\n", home_dir, + sampass->private.home_dir)); + + sampass->private.home_dir = talloc_strdup(sampass->mem_ctx, home_dir); + + return NT_STATUS_OK; +} + +/********************************************************************* + Set the account's unix home directory. + ********************************************************************/ + +NTSTATUS sam_set_account_unix_homedir(SAM_ACCOUNT_HANDLE *sampass, const char *unix_home_dir) +{ + SAM_ASSERT(sampass); + + DEBUG(10, ("sam_set_unix_homedir: setting home dir %s, was %s\n", unix_home_dir, + sampass->private.unix_home_dir)); + + sampass->private.unix_home_dir = talloc_strdup(sampass->mem_ctx, unix_home_dir); + + return NT_STATUS_OK; +} + +/********************************************************************* + Set the account's account description. + ********************************************************************/ + +NTSTATUS sam_set_account_acct_desc(SAM_ACCOUNT_HANDLE *sampass, const char *acct_desc) +{ + SAM_ASSERT(sampass); + + sampass->private.acct_desc = talloc_strdup(sampass->mem_ctx, acct_desc); + + return NT_STATUS_OK; +} + +/********************************************************************* + Set the account's workstation allowed list. + ********************************************************************/ + +NTSTATUS sam_set_account_workstations(SAM_ACCOUNT_HANDLE *sampass, const char *workstations) +{ + SAM_ASSERT(sampass); + + DEBUG(10, ("sam_set_workstations: setting workstations %s, was %s\n", workstations, + sampass->private.workstations)); + + sampass->private.workstations = talloc_strdup(sampass->mem_ctx, workstations); + + return NT_STATUS_OK; +} + +/********************************************************************* + Set the account's 'unknown_str', whatever the heck this actually is... + ********************************************************************/ + +NTSTATUS sam_set_account_unknown_str(SAM_ACCOUNT_HANDLE *sampass, const char *unknown_str) +{ + SAM_ASSERT(sampass); + + sampass->private.unknown_str = talloc_strdup(sampass->mem_ctx, unknown_str); + + return NT_STATUS_OK; +} + +/********************************************************************* + Set the account's dial string. + ********************************************************************/ + +NTSTATUS sam_set_account_munged_dial(SAM_ACCOUNT_HANDLE *sampass, const char *munged_dial) +{ + SAM_ASSERT(sampass); + + sampass->private.munged_dial = talloc_strdup(sampass->mem_ctx, munged_dial); + + return NT_STATUS_OK; +} + +/********************************************************************* + Set the account's NT hash. + ********************************************************************/ + +NTSTATUS sam_set_account_nt_pwd(SAM_ACCOUNT_HANDLE *sampass, const DATA_BLOB data) +{ + SAM_ASSERT(sampass); + + sampass->private.nt_pw = data; + + return NT_STATUS_OK; +} + +/********************************************************************* + Set the account's LM hash. + ********************************************************************/ + +NTSTATUS sam_set_account_lm_pwd(SAM_ACCOUNT_HANDLE *sampass, const DATA_BLOB data) +{ + SAM_ASSERT(sampass); + + sampass->private.lm_pw = data; + + return NT_STATUS_OK; +} + +/********************************************************************* + Set the account's plaintext password only (base procedure, see helper + below) + ********************************************************************/ + +NTSTATUS sam_set_account_plaintext_pwd(SAM_ACCOUNT_HANDLE *sampass, const char *plain_pwd) +{ + SAM_ASSERT(sampass); + + sampass->private.plaintext_pw = talloc_strdup(sampass->mem_ctx, plain_pwd); + + return NT_STATUS_OK; +} + +NTSTATUS sam_set_account_unknown_1(SAM_ACCOUNT_HANDLE *sampass, uint32 unkn) +{ + SAM_ASSERT(sampass); + + sampass->private.unknown_1 = unkn; + + return NT_STATUS_OK; +} + +NTSTATUS sam_set_account_unknown_2(SAM_ACCOUNT_HANDLE *sampass, uint32 unkn) +{ + SAM_ASSERT(sampass); + + sampass->private.unknown_2 = unkn; + + return NT_STATUS_OK; +} + +NTSTATUS sam_set_account_unknown_3(SAM_ACCOUNT_HANDLE *sampass, uint32 unkn) +{ + SAM_ASSERT(sampass); + + sampass->private.unknown_3 = unkn; + return NT_STATUS_OK; +} + +NTSTATUS sam_set_account_hours(SAM_ACCOUNT_HANDLE *sampass, const uint8 *hours) +{ + SAM_ASSERT(sampass); + + if (!hours) { + memset ((char *)sampass->private.hours, 0, MAX_HOURS_LEN); + return NT_STATUS_OK; + } + + memcpy(sampass->private.hours, hours, MAX_HOURS_LEN); + + return NT_STATUS_OK; +} + +/* Helpful interfaces to the above */ + +/********************************************************************* + Sets the last changed times and must change times for a normal + password change. + ********************************************************************/ + +NTSTATUS sam_set_account_pass_changed_now(SAM_ACCOUNT_HANDLE *sampass) +{ + uint32 expire; + NTTIME temptime; + + SAM_ASSERT(sampass); + + unix_to_nt_time(&temptime, time(NULL)); + if (!NT_STATUS_IS_OK(sam_set_account_pass_last_set_time(sampass, temptime))) + return NT_STATUS_UNSUCCESSFUL; + + if (!account_policy_get(AP_MAX_PASSWORD_AGE, &expire) + || (expire==(uint32)-1)) { + + get_nttime_max(&temptime); + if (!NT_STATUS_IS_OK(sam_set_account_pass_must_change_time(sampass, temptime, False))) + return NT_STATUS_UNSUCCESSFUL; + + } else { + /* FIXME: Add expire to temptime */ + + if (!NT_STATUS_IS_OK(sam_get_account_pass_last_set_time(sampass,&temptime)) || !NT_STATUS_IS_OK(sam_set_account_pass_must_change_time(sampass, temptime,True))) + return NT_STATUS_UNSUCCESSFUL; + } + + return NT_STATUS_OK; +} + +/********************************************************************* + Set the account's PLAINTEXT password. Used as an interface to the above. + Also sets the last change time to NOW. + ********************************************************************/ + +NTSTATUS sam_set_account_passwd(SAM_ACCOUNT_HANDLE *sampass, const char *plaintext) +{ + DATA_BLOB data; + uchar new_lanman_p16[16]; + uchar new_nt_p16[16]; + + SAM_ASSERT(sampass && plaintext); + + nt_lm_owf_gen(plaintext, new_nt_p16, new_lanman_p16); + + data = data_blob(new_nt_p16, 16); + if (!NT_STATUS_IS_OK(sam_set_account_nt_pwd(sampass, data))) + return NT_STATUS_UNSUCCESSFUL; + + data = data_blob(new_lanman_p16, 16); + + if (!NT_STATUS_IS_OK(sam_set_account_lm_pwd(sampass, data))) + return NT_STATUS_UNSUCCESSFUL; + + if (!NT_STATUS_IS_OK(sam_set_account_plaintext_pwd(sampass, plaintext))) + return NT_STATUS_UNSUCCESSFUL; + + if (!NT_STATUS_IS_OK(sam_set_account_pass_changed_now(sampass))) + return NT_STATUS_UNSUCCESSFUL; + + return NT_STATUS_OK; +} + diff --git a/source4/sam/get_set_domain.c b/source4/sam/get_set_domain.c new file mode 100644 index 0000000000..c70a4a3f09 --- /dev/null +++ b/source4/sam/get_set_domain.c @@ -0,0 +1,263 @@ +/* + Unix SMB/CIFS implementation. + SAM_DOMAIN access routines + Copyright (C) Andrew Bartlett 2002 + Copyright (C) Stefan (metze) Metzmacher 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_SAM + +NTSTATUS sam_get_domain_sid(SAM_DOMAIN_HANDLE *domain, const DOM_SID **sid) +{ + SAM_ASSERT(domain &&sid); + + *sid = &(domain->private.sid); + + return NT_STATUS_OK; +} + +NTSTATUS sam_get_domain_num_accounts(SAM_DOMAIN_HANDLE *domain, uint32 *num_accounts) +{ + SAM_ASSERT(domain &&num_accounts); + + *num_accounts = domain->private.num_accounts; + + return NT_STATUS_OK; +} + +NTSTATUS sam_get_domain_num_groups(SAM_DOMAIN_HANDLE *domain, uint32 *num_groups) +{ + SAM_ASSERT(domain &&num_groups); + + *num_groups = domain->private.num_groups; + + return NT_STATUS_OK; +} + +NTSTATUS sam_get_domain_num_aliases(SAM_DOMAIN_HANDLE *domain, uint32 *num_aliases) +{ + SAM_ASSERT(domain &&num_aliases); + + *num_aliases = domain->private.num_aliases; + + return NT_STATUS_OK; +} + +NTSTATUS sam_get_domain_name(SAM_DOMAIN_HANDLE *domain, const char **domain_name) +{ + SAM_ASSERT(domain &&domain_name); + + *domain_name = domain->private.name; + + return NT_STATUS_OK; +} + +NTSTATUS sam_get_domain_server(SAM_DOMAIN_HANDLE *domain, const char **server_name) +{ + SAM_ASSERT(domain &&server_name); + + *server_name = domain->private.servername; + + return NT_STATUS_OK; +} + +NTSTATUS sam_get_domain_max_pwdage(SAM_DOMAIN_HANDLE *domain, NTTIME *max_passwordage) +{ + SAM_ASSERT(domain &&max_passwordage); + + *max_passwordage = domain->private.max_passwordage; + + return NT_STATUS_OK; +} + +NTSTATUS sam_get_domain_min_pwdage(SAM_DOMAIN_HANDLE *domain, NTTIME *min_passwordage) +{ + SAM_ASSERT(domain &&min_passwordage); + + *min_passwordage = domain->private.min_passwordage; + + return NT_STATUS_OK; +} + +NTSTATUS sam_get_domain_lockout_duration(SAM_DOMAIN_HANDLE *domain, NTTIME *lockout_duration) +{ + SAM_ASSERT(domain &&lockout_duration); + + *lockout_duration = domain->private.lockout_duration; + + return NT_STATUS_OK; +} + +NTSTATUS sam_get_domain_reset_count(SAM_DOMAIN_HANDLE *domain, NTTIME *reset_lockout_count) +{ + SAM_ASSERT(domain &&reset_lockout_count); + + *reset_lockout_count = domain->private.reset_count; + + return NT_STATUS_OK; +} + +NTSTATUS sam_get_domain_min_pwdlength(SAM_DOMAIN_HANDLE *domain, uint16 *min_passwordlength) +{ + SAM_ASSERT(domain &&min_passwordlength); + + *min_passwordlength = domain->private.min_passwordlength; + + return NT_STATUS_OK; +} + +NTSTATUS sam_get_domain_pwd_history(SAM_DOMAIN_HANDLE *domain, uint16 *password_history) +{ + SAM_ASSERT(domain &&password_history); + + *password_history = domain->private.password_history; + + return NT_STATUS_OK; +} + +NTSTATUS sam_get_domain_lockout_count(SAM_DOMAIN_HANDLE *domain, uint16 *lockout_count) +{ + SAM_ASSERT(domain &&lockout_count); + + *lockout_count = domain->private.lockout_count; + + return NT_STATUS_OK; +} + +NTSTATUS sam_get_domain_force_logoff(SAM_DOMAIN_HANDLE *domain, BOOL *force_logoff) +{ + SAM_ASSERT(domain &&force_logoff); + + *force_logoff = domain->private.force_logoff; + + return NT_STATUS_OK; +} + + +NTSTATUS sam_get_domain_login_pwdchange(SAM_DOMAIN_HANDLE *domain, BOOL *login_pwdchange) +{ + SAM_ASSERT(domain && login_pwdchange); + + *login_pwdchange = domain->private.login_pwdchange; + + return NT_STATUS_OK; +} + +/* Set */ + +NTSTATUS sam_set_domain_name(SAM_DOMAIN_HANDLE *domain, const char *domain_name) +{ + SAM_ASSERT(domain); + + domain->private.name = talloc_strdup(domain->mem_ctx, domain_name); + + return NT_STATUS_OK; +} + + +NTSTATUS sam_set_domain_max_pwdage(SAM_DOMAIN_HANDLE *domain, NTTIME max_passwordage) +{ + SAM_ASSERT(domain); + + domain->private.max_passwordage = max_passwordage; + + return NT_STATUS_OK; +} + +NTSTATUS sam_set_domain_min_pwdage(SAM_DOMAIN_HANDLE *domain, NTTIME min_passwordage) +{ + SAM_ASSERT(domain); + + domain->private.min_passwordage = min_passwordage; + + return NT_STATUS_OK; +} + +NTSTATUS sam_set_domain_lockout_duration(SAM_DOMAIN_HANDLE *domain, NTTIME lockout_duration) +{ + SAM_ASSERT(domain); + + domain->private.lockout_duration = lockout_duration; + + return NT_STATUS_OK; +} +NTSTATUS sam_set_domain_reset_count(SAM_DOMAIN_HANDLE *domain, NTTIME reset_lockout_count) +{ + SAM_ASSERT(domain); + + domain->private.reset_count = reset_lockout_count; + + return NT_STATUS_OK; +} + +NTSTATUS sam_set_domain_min_pwdlength(SAM_DOMAIN_HANDLE *domain, uint16 min_passwordlength) +{ + SAM_ASSERT(domain); + + domain->private.min_passwordlength = min_passwordlength; + + return NT_STATUS_OK; +} + +NTSTATUS sam_set_domain_pwd_history(SAM_DOMAIN_HANDLE *domain, uint16 password_history) +{ + SAM_ASSERT(domain); + + domain->private.password_history = password_history; + + return NT_STATUS_OK; +} + +NTSTATUS sam_set_domain_lockout_count(SAM_DOMAIN_HANDLE *domain, uint16 lockout_count) +{ + SAM_ASSERT(domain); + + domain->private.lockout_count = lockout_count; + + return NT_STATUS_OK; +} + +NTSTATUS sam_set_domain_force_logoff(SAM_DOMAIN_HANDLE *domain, BOOL force_logoff) +{ + SAM_ASSERT(domain); + + domain->private.force_logoff = force_logoff; + + return NT_STATUS_OK; +} + +NTSTATUS sam_set_domain_login_pwdchange(SAM_DOMAIN_HANDLE *domain, BOOL login_pwdchange) +{ + SAM_ASSERT(domain); + + domain->private.login_pwdchange = login_pwdchange; + + return NT_STATUS_OK; +} + +NTSTATUS sam_set_domain_server(SAM_DOMAIN_HANDLE *domain, const char *server_name) +{ + SAM_ASSERT(domain); + + domain->private.servername = talloc_strdup(domain->mem_ctx, server_name); + + return NT_STATUS_OK; +} diff --git a/source4/sam/get_set_group.c b/source4/sam/get_set_group.c new file mode 100644 index 0000000000..11ea9258a7 --- /dev/null +++ b/source4/sam/get_set_group.c @@ -0,0 +1,106 @@ +/* + Unix SMB/CIFS implementation. + SAM_USER_HANDLE access routines + Copyright (C) Andrew Bartlett 2002 + Copyright (C) Stefan (metze) Metzmacher 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_SAM + +/* sam group get functions */ + +NTSTATUS sam_get_group_sid(const SAM_GROUP_HANDLE *group, const DOM_SID **sid) +{ + SAM_ASSERT(group && sid); + + *sid = &(group->private.sid); + + return NT_STATUS_OK; +} + +NTSTATUS sam_get_group_ctrl(const SAM_GROUP_HANDLE *group, uint32 *group_ctrl) +{ + SAM_ASSERT(group && group_ctrl); + + *group_ctrl = group->private.group_ctrl; + + return NT_STATUS_OK; +} + +NTSTATUS sam_get_group_name(const SAM_GROUP_HANDLE *group, const char **group_name) +{ + SAM_ASSERT(group); + + *group_name = group->private.group_name; + + return NT_STATUS_OK; + +} +NTSTATUS sam_get_group_comment(const SAM_GROUP_HANDLE *group, const char **group_desc) +{ + SAM_ASSERT(group); + + *group_desc = group->private.group_desc; + + return NT_STATUS_OK; +} + +/* sam group set functions */ + +NTSTATUS sam_set_group_sid(SAM_GROUP_HANDLE *group, const DOM_SID *sid) +{ + SAM_ASSERT(group); + + if (!sid) + ZERO_STRUCT(group->private.sid); + else + sid_copy(&(group->private.sid), sid); + + return NT_STATUS_OK; +} + +NTSTATUS sam_set_group_group_ctrl(SAM_GROUP_HANDLE *group, uint32 group_ctrl) +{ + SAM_ASSERT(group); + + group->private.group_ctrl = group_ctrl; + + return NT_STATUS_OK; +} + +NTSTATUS sam_set_group_name(SAM_GROUP_HANDLE *group, const char *group_name) +{ + SAM_ASSERT(group); + + group->private.group_name = talloc_strdup(group->mem_ctx, group_name); + + return NT_STATUS_OK; +} + +NTSTATUS sam_set_group_description(SAM_GROUP_HANDLE *group, const char *group_desc) +{ + SAM_ASSERT(group); + + group->private.group_desc = talloc_strdup(group->mem_ctx, group_desc); + + return NT_STATUS_OK; + +} diff --git a/source4/sam/group.c b/source4/sam/group.c new file mode 100644 index 0000000000..101e3dd7ce --- /dev/null +++ b/source4/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/source4/sam/gumm_tdb.c b/source4/sam/gumm_tdb.c new file mode 100644 index 0000000000..52eaab9e17 --- /dev/null +++ b/source4/sam/gumm_tdb.c @@ -0,0 +1,562 @@ +/* + * 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" +#include "gums.h" +#include "tdbsam2.h" +#include "tdbsam2_parse_info.h" + +static int tdbgumm_debug_level = DBGC_ALL; +#undef DBGC_CLASS +#define DBGC_CLASS tdbgumm_debug_level + +#define TDBSAM_VERSION "20021215" +#define TDB_FILE_NAME "tdbsam2.tdb" +#define DOMAINPREFIX "DOMAIN_" +#define OBJECTPREFIX "OBJECT_" +#define SIDPREFIX "SID_" +#define PRIVILEGEPREFIX "PRIV_" + +#define TDB_FORMAT_STRING "ddB" + +union tdbsam2_data { + struct tdbsam2_domain_data *domain; + struct tdbsam2_user_data *user; + struct tdbsam2_group_data *group; +}; + +struct tdbsam2_object { + uint32 type; + union tdbsam2_data data; +}; + +static TDB_CONTEXT *tdbsam2_db; + +#define TALLOC_CHECK(ptr, err, label) do { if ((ptr) == NULL) { DEBUG(0, ("%s: Out of memory!\n", __FUNCTION__)); err = NT_STATUS_NO_MEMORY; goto label; } } while(0) +#define SET_OR_FAIL(func, label) do { if (NT_STATUS_IS_ERR(func)) { DEBUG(0, ("%s: Setting gums object data failed!\n", __FUNCTION__)); goto label; } } while(0) + +static NTSTATUS init_tdbsam2_object_from_buffer(struct tdbsam2_object *object, TALLOC_CTX *mem_ctx, char *buffer, int size) { + + return NT_STATUS_OK; +} + +static NTSTATUS tdbsam2_opentdb(void) { + + return NT_STATUS_OK; +} + +static NTSTATUS tdbsam2_get_object_by_name(struct tdbsam2_object *obj, TALLOC_CTX *mem_ctx, const char* name) { + + NTSTATUS ret; + TDB_DATA data, key; + fstring keystr; + fstring objname; + + if (!obj || !mem_ctx || !name) + return NT_STATUS_INVALID_PARAMETER; + + if (tdbsam2_db == NULL) { + if (NT_STATUS_IS_ERR(ret = tdbsam2_opentdb())) { + goto done; + } + } + + unix_strlower(name, -1, objname, sizeof(objname)); + + slprintf(keystr, sizeof(keystr)-1, "%s%s", OBJECTPREFIX, objname); + key.dptr = keystr; + key.dsize = strlen(keystr) + 1; + + data = tdb_fetch(tdbsam2_db, key); + if (!data.dptr) { + DEBUG(5, ("get_domain_sid: Error fetching database, domain entry not found!\n")); + DEBUGADD(5, (" Error: %s\n", tdb_errorstr(tdbsam2_db))); + DEBUGADD(5, (" Key: %s\n", keystr)); + ret = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + if (NT_STATUS_IS_ERR(init_tdbsam2_object_from_buffer(obj, mem_ctx, data.dptr, data.dsize))) { + SAFE_FREE(data.dptr); + DEBUG(0, ("get_domain_sid: Error fetching database, malformed entry!\n")); + ret = NT_STATUS_UNSUCCESSFUL; + goto done; + } + SAFE_FREE(data.dptr); + + ret = NT_STATUS_OK; + +done: + return ret; +} + + +static NTSTATUS tdbsam2_store(struct tdbsam2_object *object) { + + NTSTATUS ret; + + return NT_STATUS_OK; +} + +static NTSTATUS tdbsam2_get_next_sid(TALLOC_CTX *mem_ctx, DOM_SID *sid) { + + NTSTATUS ret; + + return NT_STATUS_OK; +} + +static NTSTATUS tdbsam2_user_data_to_gums_object(GUMS_OBJECT **object, struct tdbsam2_user_data *userdata, uint32 type) { + + NTSTATUS ret; + + if (!object || !userdata) { + DEBUG(0, ("tdbsam2_user_data_to_gums_object: no NULL pointers are accepted here!\n")); + return NT_STATUS_UNSUCCESSFUL; + } + + /* userdata->xcounter */ + /* userdata->sec_desc */ + + SET_OR_FAIL(gums_set_object_sid(*object, userdata->user_sid), error); + SET_OR_FAIL(gums_set_object_name(*object, userdata->name), error); + + SET_OR_FAIL(gums_set_user_pri_group(*object, userdata->group_sid), error); + + if (userdata->description) + SET_OR_FAIL(gums_set_object_description(*object, userdata->description), error); + + if (userdata->full_name) + SET_OR_FAIL(gums_set_user_fullname(*object, userdata->full_name), error); + + if (userdata->home_dir) + SET_OR_FAIL(gums_set_user_homedir(*object, userdata->home_dir), error); + + if (userdata->dir_drive) + SET_OR_FAIL(gums_set_user_dir_drive(*object, userdata->dir_drive), error); + + if (userdata->logon_script) + SET_OR_FAIL(gums_set_user_logon_script(*object, userdata->logon_script), error); + + if (userdata->profile_path) + SET_OR_FAIL(gums_set_user_profile_path(*object, userdata->profile_path), error); + + if (userdata->workstations) + SET_OR_FAIL(gums_set_user_workstations(*object, userdata->workstations), error); + + if (userdata->unknown_str) + SET_OR_FAIL(gums_set_user_unknown_str(*object, userdata->unknown_str), error); + + if (userdata->munged_dial) + SET_OR_FAIL(gums_set_user_munged_dial(*object, userdata->munged_dial), error); + + SET_OR_FAIL(gums_set_user_logon_divs(*object, userdata->logon_divs), error); + SET_OR_FAIL(gums_set_user_hours_len(*object, userdata->hours_len), error); + + if (userdata->hours) + SET_OR_FAIL(gums_set_user_hours(*object, userdata->hours), error); + + SET_OR_FAIL(gums_set_user_unknown_3(*object, userdata->unknown_3), error); + SET_OR_FAIL(gums_set_user_unknown_5(*object, userdata->unknown_5), error); + SET_OR_FAIL(gums_set_user_unknown_6(*object, userdata->unknown_6), error); + + SET_OR_FAIL(gums_set_user_logon_time(*object, userdata->logon_time), error); + SET_OR_FAIL(gums_set_user_logoff_time(*object, userdata->logoff_time), error); + SET_OR_FAIL(gums_set_user_kickoff_time(*object, userdata->kickoff_time), error); + SET_OR_FAIL(gums_set_user_pass_last_set_time(*object, userdata->pass_last_set_time), error); + SET_OR_FAIL(gums_set_user_pass_can_change_time(*object, userdata->pass_can_change_time), error); + SET_OR_FAIL(gums_set_user_pass_must_change_time(*object, userdata->pass_must_change_time), error); + + ret = NT_STATUS_OK; + return ret; + +error: + talloc_destroy((*object)->mem_ctx); + *object = NULL; + return ret; +} + +static NTSTATUS tdbsam2_group_data_to_gums_object(GUMS_OBJECT **object, struct tdbsam2_group_data *groupdata, uint32 type) { + + NTSTATUS ret; + + if (!object || !groupdata) { + DEBUG(0, ("tdbsam2_group_data_to_gums_object: no NULL pointers are accepted here!\n")); + return NT_STATUS_UNSUCCESSFUL; + } + + /* groupdata->xcounter */ + /* groupdata->sec_desc */ + + SET_OR_FAIL(gums_set_object_sid(*object, groupdata->group_sid), error); + SET_OR_FAIL(gums_set_object_name(*object, groupdata->name), error); + + if (groupdata->description) + SET_OR_FAIL(gums_set_object_description(*object, groupdata->description), error); + + if (groupdata->count) + SET_OR_FAIL(gums_set_group_members(*object, groupdata->count, groupdata->members), error); + + ret = NT_STATUS_OK; + return ret; + +error: + talloc_destroy((*object)->mem_ctx); + *object = NULL; + return ret; +} + +static NTSTATUS tdbsam2_domain_data_to_gums_object(GUMS_OBJECT **object, struct tdbsam2_domain_data *domdata, uint32 type) { + + NTSTATUS ret; + + if (!object || !domdata) { + DEBUG(0, ("tdbsam2_domain_data_to_gums_object: no NULL pointers are accepted here!\n")); + return NT_STATUS_UNSUCCESSFUL; + } + + /* domdata->xcounter */ + /* domdata->sec_desc */ + + SET_OR_FAIL(gums_set_object_sid(*object, domdata->dom_sid), error); + SET_OR_FAIL(gums_set_object_name(*object, domdata->name), error); + + if (domdata->description) + SET_OR_FAIL(gums_set_object_description(*object, domdata->description), error); + + ret = NT_STATUS_OK; + return ret; + +error: + talloc_destroy((*object)->mem_ctx); + *object = NULL; + return ret; +} + +static NTSTATUS tdbsam2_data_to_gums_object(GUMS_OBJECT **object, struct tdbsam2_object *data) { + + NTSTATUS ret; + + if (!object || !data) { + DEBUG(0, ("tdbsam2_user_data_to_gums_object: no NULL structure pointers are accepted here!\n")); + ret = NT_STATUS_INVALID_PARAMETER; + goto done; + } + + ret = gums_create_object(object, data->type); + if (NT_STATUS_IS_ERR(ret)) { + DEBUG(5, ("tdbsam2_user_data_to_gums_object: error creating gums object!\n")); + goto done; + } + + switch (data->type) { + case GUMS_OBJ_DOMAIN: + ret = tdbsam2_domain_data_to_gums_object(object, data->data.domain, data->type); + break; + + case GUMS_OBJ_NORMAL_USER: + ret = tdbsam2_user_data_to_gums_object(object, data->data.user, data->type); + break; + + case GUMS_OBJ_GROUP: + case GUMS_OBJ_ALIAS: + ret = tdbsam2_group_data_to_gums_object(object, data->data.group, data->type); + break; + + default: + ret = NT_STATUS_UNSUCCESSFUL; + } + +done: + return ret; +} + + + + + +/* GUMM object functions */ + +static NTSTATUS get_domain_sid(DOM_SID *sid, const char* name) { + + NTSTATUS ret; + struct tdbsam2_object obj; + TALLOC_CTX *mem_ctx; + TDB_DATA data, key; + fstring keystr; + fstring domname; + + if (!sid || !name) + return NT_STATUS_INVALID_PARAMETER; + + mem_ctx = talloc_init("get_domain_sid"); + if (!mem_ctx) { + DEBUG(0, ("tdbsam2_new_object: Out of memory!\n")); + return NT_STATUS_NO_MEMORY; + } + + if (tdbsam2_db == NULL) { + if (NT_STATUS_IS_ERR(ret = tdbsam2_opentdb())) { + goto done; + } + } + + unix_strlower(name, -1, domname, sizeof(domname)); + + slprintf(keystr, sizeof(keystr)-1, "%s%s", DOMAINPREFIX, domname); + key.dptr = keystr; + key.dsize = strlen(keystr) + 1; + + data = tdb_fetch(tdbsam2_db, key); + if (!data.dptr) { + DEBUG(5, ("get_domain_sid: Error fetching database, domain entry not found!\n")); + DEBUGADD(5, (" Error: %s\n", tdb_errorstr(tdbsam2_db))); + DEBUGADD(5, (" Key: %s\n", keystr)); + ret = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + if (NT_STATUS_IS_ERR(init_tdbsam2_object_from_buffer(&obj, mem_ctx, data.dptr, data.dsize))) { + SAFE_FREE(data.dptr); + DEBUG(0, ("get_domain_sid: Error fetching database, malformed entry!\n")); + ret = NT_STATUS_UNSUCCESSFUL; + goto done; + } + SAFE_FREE(data.dptr); + + if (obj.type != GUMS_OBJ_DOMAIN) { + DEBUG(5, ("get_domain_sid: Requested object is not a domain!\n")); + ret = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + sid_copy(sid, obj.data.domain->dom_sid); + + ret = NT_STATUS_OK; + +done: + if (mem_ctx) talloc_destroy(mem_ctx); + return ret; +} + + NTSTATUS (*set_domain_sid) (const DOM_SID *sid, const char *name); + + NTSTATUS (*get_sequence_number) (void); + + +static NTSTATUS tdbsam2_new_object(DOM_SID **sid, const char *name, const int obj_type) { + + NTSTATUS ret; + struct tdbsam2_object obj; + TALLOC_CTX *mem_ctx; + + if (!sid || !name) { + DEBUG(0, ("tdbsam2_new_object: no NULL pointers are accepted here!\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + mem_ctx = talloc_init("tdbsam2_new_object"); + if (!mem_ctx) { + DEBUG(0, ("tdbsam2_new_object: Out of memory!\n")); + return NT_STATUS_NO_MEMORY; + } + + switch (obj_type) { + case GUMS_OBJ_NORMAL_USER: + obj.data.user = (struct tdbsam2_user_data *)talloc_zero(mem_ctx, sizeof(struct tdbsam2_user_data)); + TALLOC_CHECK(obj.data.user, ret, done); + + /*obj.data.user->sec_desc*/ + + tdbsam2_get_next_sid(mem_ctx, obj.data.user->user_sid); + TALLOC_CHECK(obj.data.user->user_sid, ret, done); + + obj.data.user->name = talloc_strdup(mem_ctx, name); + TALLOC_CHECK(obj.data.user, ret, done); + + break; + + case GUMS_OBJ_GROUP: + case GUMS_OBJ_ALIAS: + obj.data.group = (struct tdbsam2_group_data *)talloc_zero(mem_ctx, sizeof(struct tdbsam2_group_data)); + TALLOC_CHECK(obj.data.group, ret, done); + + /*obj.data.user->sec_desc*/ + + tdbsam2_get_next_sid(mem_ctx, obj.data.group->group_sid); + TALLOC_CHECK(obj.data.group->group_sid, ret, done); + + obj.data.group->name = talloc_strdup(mem_ctx, name); + TALLOC_CHECK(obj.data.group, ret, done); + + break; + + case GUMS_OBJ_DOMAIN: + /* TODO: SHOULD WE ALLOW TO CREATE NEW DOMAINS ? */ + + default: + ret = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + ret = tdbsam2_store(&obj); + +done: + talloc_destroy(mem_ctx); + return ret; +} + +static NTSTATUS tdbsam2_delete_object(const DOM_SID *sid) { + + NTSTATUS ret; + struct tdbsam2_object obj; + TALLOC_CTX *mem_ctx; + TDB_DATA data, key; + fstring keystr; + fstring sidstr; + char *obj_name = NULL; + int obj_type, obj_version, len; + + if (!sid) { + DEBUG(0, ("tdbsam2_new_object: no NULL pointers are accepted here!\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + mem_ctx = talloc_init("tdbsam2_delete_object"); + if (!mem_ctx) { + DEBUG(0, ("tdbsam2_new_object: Out of memory!\n")); + return NT_STATUS_NO_MEMORY; + } + + if (tdbsam2_db == NULL) { + if (NT_STATUS_IS_ERR(ret = tdbsam2_opentdb())) { + goto done; + } + } + + sid_to_string(sidstr, sid); + + slprintf(keystr, sizeof(keystr)-1, "%s%s", SIDPREFIX, sidstr); + key.dptr = keystr; + key.dsize = strlen(keystr) + 1; + + data = tdb_fetch(tdbsam2_db, key); + if (!data.dptr) { + DEBUG(5, ("get_domain_sid: Error fetching database, SID entry not found!\n")); + DEBUGADD(5, (" Error: %s\n", tdb_errorstr(tdbsam2_db))); + DEBUGADD(5, (" Key: %s\n", keystr)); + ret = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + len = tdb_unpack(data.dptr, data.dsize, TDB_FORMAT_STRING, + &obj_version, + &obj_type, + &obj_name); + + if (len == -1) { + ret = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + if (tdb_delete(tdbsam2_db, key) != TDB_SUCCESS) { + DEBUG(5, ("tdbsam2_object_delete: Error deleting object!\n")); + DEBUGADD(5, (" Error: %s\n", tdb_errorstr(tdbsam2_db))); + DEBUGADD(5, (" Key: %s\n", keystr)); + ret = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + switch (obj_type) { + case GUMS_OBJ_NORMAL_USER: + case GUMS_OBJ_GROUP: + case GUMS_OBJ_ALIAS: + + slprintf(keystr, sizeof(keystr)-1, "%s%s", OBJECTPREFIX, obj_name); + key.dptr = keystr; + key.dsize = strlen(keystr) + 1; + + if (tdb_delete(tdbsam2_db, key) != TDB_SUCCESS) { + DEBUG(5, ("tdbsam2_object_delete: Error deleting object!\n")); + DEBUGADD(5, (" Error: %s\n", tdb_errorstr(tdbsam2_db))); + DEBUGADD(5, (" Key: %s\n", keystr)); + ret = NT_STATUS_UNSUCCESSFUL; + goto done; + } + break; + + case GUMS_OBJ_DOMAIN: + /* TODO: SHOULD WE ALLOW TO DELETE DOMAINS ? */ + + default: + ret = NT_STATUS_UNSUCCESSFUL; + goto done; + } + +done: + SAFE_FREE(obj_name); + talloc_destroy(mem_ctx); + return ret; +} + + NTSTATUS (*get_object_from_sid) (GUMS_OBJECT **object, const DOM_SID *sid, const int obj_type); + NTSTATUS (*get_sid_from_name) (GUMS_OBJECT **object, const char *name); + /* 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) (const 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_memberss_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 (*add_members_to_privilege) (const LUID_ATTR *priv, const DOM_SID **members); + NTSTATUS (*delete_members_from_privilege) (const LUID_ATTR *priv, const DOM_SID **members); + NTSTATUS (*enumerate_privilege_members) (DOM_SID **members, const LUID_ATTR *priv); + NTSTATUS (*get_sid_privileges) (DOM_SID **privs, const DOM_SID *sid); + /* warning!: set_privilege will overwrite a prior existing privilege if such exist */ + NTSTATUS (*set_privilege) (GUMS_PRIVILEGE *priv); + + +int gumm_init(GUMS_FUNCTIONS **storage) { + + return 0; +} diff --git a/source4/sam/gums.c b/source4/sam/gums.c new file mode 100644 index 0000000000..3a20ef6fc9 --- /dev/null +++ b/source4/sam/gums.c @@ -0,0 +1,131 @@ +/* + 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_GUMS*/ + +#define GMV_MAJOR 0 +#define GMV_MINOR 1 + +GUMS_FUNCTIONS *gums_storage; +static void *dl_handle; + +PRIVS privs[] = { + {PRIV_NONE, "no_privs", "No privilege"}, /* this one MUST be first */ + {PRIV_CREATE_TOKEN, "SeCreateToken", "Create Token"}, + {PRIV_ASSIGNPRIMARYTOKEN, "SeAssignPrimaryToken", "Assign Primary Token"}, + {PRIV_LOCK_MEMORY, "SeLockMemory", "Lock Memory"}, + {PRIV_INCREASE_QUOTA, "SeIncreaseQuotaPrivilege", "Increase Quota Privilege"}, + {PRIV_MACHINE_ACCOUNT, "SeMachineAccount", "Machine Account"}, + {PRIV_TCB, "SeTCB", "TCB"}, + {PRIV_SECURITY, "SeSecurityPrivilege", "Security Privilege"}, + {PRIV_TAKE_OWNERSHIP, "SeTakeOwnershipPrivilege", "Take Ownership Privilege"}, + {PRIV_LOAD_DRIVER, "SeLocalDriverPrivilege", "Local Driver Privilege"}, + {PRIV_SYSTEM_PROFILE, "SeSystemProfilePrivilege", "System Profile Privilege"}, + {PRIV_SYSTEMTIME, "SeSystemtimePrivilege", "System Time"}, + {PRIV_PROF_SINGLE_PROCESS, "SeProfileSingleProcessPrivilege", "Profile Single Process Privilege"}, + {PRIV_INC_BASE_PRIORITY, "SeIncreaseBasePriorityPrivilege", "Increase Base Priority Privilege"}, + {PRIV_CREATE_PAGEFILE, "SeCreatePagefilePrivilege", "Create Pagefile Privilege"}, + {PRIV_CREATE_PERMANENT, "SeCreatePermanent", "Create Permanent"}, + {PRIV_BACKUP, "SeBackupPrivilege", "Backup Privilege"}, + {PRIV_RESTORE, "SeRestorePrivilege", "Restore Privilege"}, + {PRIV_SHUTDOWN, "SeShutdownPrivilege", "Shutdown Privilege"}, + {PRIV_DEBUG, "SeDebugPrivilege", "Debug Privilege"}, + {PRIV_AUDIT, "SeAudit", "Audit"}, + {PRIV_SYSTEM_ENVIRONMENT, "SeSystemEnvironmentPrivilege", "System Environment Privilege"}, + {PRIV_CHANGE_NOTIFY, "SeChangeNotify", "Change Notify"}, + {PRIV_REMOTE_SHUTDOWN, "SeRemoteShutdownPrivilege", "Remote Shutdown Privilege"}, + {PRIV_UNDOCK, "SeUndock", "Undock"}, + {PRIV_SYNC_AGENT, "SeSynchronizationAgent", "Synchronization Agent"}, + {PRIV_ENABLE_DELEGATION, "SeEnableDelegation", "Enable Delegation"}, + {PRIV_ALL, "SaAllPrivs", "All Privileges"} +}; + +NTSTATUS gums_init(const char *module_name) +{ + int (*module_version)(int); + NTSTATUS (*module_init)(); +/* gums_module_init module_init;*/ + NTSTATUS ret = NT_STATUS_UNSUCCESSFUL; + + DEBUG(5, ("Opening gums module %s\n", module_name)); + dl_handle = sys_dlopen(module_name, RTLD_NOW); + if (!dl_handle) { + DEBUG(0, ("ERROR: Failed to load gums module %s, error: %s\n", module_name, sys_dlerror())); + return NT_STATUS_UNSUCCESSFUL; + } + + module_version = sys_dlsym(dl_handle, "gumm_version"); + if (!module_version) { + DEBUG(0, ("ERROR: Failed to find gums module version!\n")); + goto error; + } + + if (module_version(GMV_MAJOR) != GUMS_VERSION_MAJOR) { + DEBUG(0, ("ERROR: Module's major version does not match gums version!\n")); + goto error; + } + + if (module_version(GMV_MINOR) != GUMS_VERSION_MINOR) { + DEBUG(1, ("WARNING: Module's minor version does not match gums version!\n")); + } + + module_init = sys_dlsym(dl_handle, "gumm_init"); + if (!module_init) { + DEBUG(0, ("ERROR: Failed to find gums module's init function!\n")); + goto error; + } + + DEBUG(5, ("Initializing module %s\n", module_name)); + + ret = module_init(&gums_storage); + goto done; + +error: + ret = NT_STATUS_UNSUCCESSFUL; + sys_dlclose(dl_handle); + +done: + return ret; +} + +NTSTATUS gums_unload(void) +{ + NSTATUS ret; + NTSTATUS (*module_finalize)(); + + if (!dl_handle) + return NT_STATUS_UNSUCCESSFUL; + + module_close = sys_dlsym(dl_handle, "gumm_finalize"); + if (!module_finalize) { + DEBUG(0, ("ERROR: Failed to find gums module's init function!\n")); + return NT_STATUS_UNSUCCESSFUL; + } + + DEBUG(5, ("Finalizing module %s\n", module_name)); + + ret = module_finalize(); + sys_dlclose(dl_handle); + + return ret; +} diff --git a/source4/sam/gums_api.c b/source4/sam/gums_api.c new file mode 100644 index 0000000000..75e32fa861 --- /dev/null +++ b/source4/sam/gums_api.c @@ -0,0 +1,1268 @@ +/* + 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" + +extern GUMS_FUNCTIONS *gums_storage; + +/* Functions to get/set info from a GUMS object */ + +NTSTATUS gums_get_object_type(uint32 *type, const GUMS_OBJECT *obj) +{ + if (!obj) + return NT_STATUS_INVALID_PARAMETER; + + *type = obj->type; + return NT_STATUS_OK; +} + +NTSTATUS gums_create_object(GUMS_OBJECT **obj, uint32 type) +{ + TALLOC_CTX *mem_ctx = talloc_init("gums_create_object"); + GUMS_OBJECT *go; + NT_STATUS ret; + + go = talloc_zero(mem_ctx, sizeof(GUMS_OBJECT)); + go->mem_ctx = mem_ctx; + go->type = type; + go->version = GUMS_OBJECT_VERSION; + + switch(type) { + case GUMS_OBJ_DOMAIN: + break; + +/* + case GUMS_OBJ_WORKSTATION_TRUST: + case GUMS_OBJ_SERVER_TRUST: + case GUMS_OBJ_DOMAIN_TRUST: +*/ + case GUMS_OBJ_NORMAL_USER: + go->data = (GUMS_USER *)talloc_zero(mem_ctx, sizeof(GUMS_USER)); + break; + + case GUMS_OBJ_GROUP: + case GUMS_OBJ_ALIAS: + go->data = (GUMS_GROUP *)talloc_zero(mem_ctx, sizeof(GUMS_GROUP)); + break; + + default: + /* TODO: throw error */ + ret = NT_STATUS_OBJECT_TYPE_MISMATCH; + goto error; + } + + if (!(go->data)) { + ret = NT_STATUS_NO_MEMORY; + DEBUG(0, ("gums_create_object: Out of memory!\n")); + goto error; + } + + *obj = go; + return NT_STATUS_OK; + +error: + talloc_destroy(go->mem_ctx); + *obj = NULL; + return ret; +} + +NTSTATUS gums_get_object_seq_num(uint32 *version, const GUMS_OBJECT *obj) +{ + if (!version || !obj) + return NT_STATUS_INVALID_PARAMETER; + + *version = obj->version; + return NT_STATUS_OK; +} + +NTSTATUS gums_set_object_seq_num(GUMS_OBJECT *obj, uint32 version) +{ + if (!obj) + return NT_STATUS_INVALID_PARAMETER; + + obj->version = version; + return NT_STATUS_OK; +} + +NTSTATUS gums_get_sec_desc(SEC_DESC **sec_desc, const GUMS_OBJECT *obj) +{ + if (!sec_desc || !obj) + return NT_STATUS_INVALID_PARAMETER; + + *sec_desc = obj->sec_desc; + 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_get_object_sid(DOM_SID **sid, const GUMS_OBJECT *obj) +{ + if (!sid || !obj) + return NT_STATUS_INVALID_PARAMETER; + + *sid = obj->sid; + 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_get_object_name(char **name, const GUMS_OBJECT *obj) +{ + if (!name || !obj) + return NT_STATUS_INVALID_PARAMETER; + + *name = obj->name; + 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_get_object_description(char **description, const GUMS_OBJECT *obj) +{ + if (!description || !obj) + return NT_STATUS_INVALID_PARAMETER; + + *description = obj->description; + 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; +} + +/* User specific functions */ + +/* +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; +} +*/ + +NTSTATUS gums_get_user_pri_group(DOM_SID **sid, const GUMS_OBJECT *obj) +{ + if (!sid || !obj) + return NT_STATUS_INVALID_PARAMETER; + + if (obj->type != GUMS_OBJ_NORMAL_USER) + return NT_STATUS_OBJECT_TYPE_MISMATCH; + + *sid = obj->data.user->group_sid; + return NT_STATUS_OK; +} + +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->data.user->group_sid = sid_dup_talloc(obj->mem_ctx, sid); + if (!(obj->data.user->group_sid)) return NT_STATUS_NO_MEMORY; + return NT_STATUS_OK; +} + +NTSTATUS gums_get_user_nt_pwd(DATA_BLOB **nt_pwd, const GUMS_OBJECT *obj) +{ + if (!nt_pwd || !obj) + return NT_STATUS_INVALID_PARAMETER; + + if (obj->type != GUMS_OBJ_NORMAL_USER) + return NT_STATUS_OBJECT_TYPE_MISMATCH; + + *nt_pwd = obj->data.user->nt_pw; + return NT_STATUS_OK; +} + +NTSTATUS gums_set_user_nt_pwd(GUMS_OBJECT *obj, const DATA_BLOB nt_pwd) +{ + if (!obj || !nt_pwd || nt_pwd != NT_HASH_LEN) + return NT_STATUS_INVALID_PARAMETER; + + if (obj->type != GUMS_OBJ_NORMAL_USER) + return NT_STATUS_OBJECT_TYPE_MISMATCH; + + obj->data.user->nt_pwd = data_blob_talloc(obj->mem_ctx, nt_pwd.data, nt_pwd.lenght); + return NT_STATUS_OK; +} + +NTSTATUS gums_get_user_lm_pwd(DATA_BLOB **lm_pwd, const GUMS_OBJECT *obj) +{ + if (!lm_pwd || !obj) + return NT_STATUS_INVALID_PARAMETER; + + if (obj->type != GUMS_OBJ_NORMAL_USER) + return NT_STATUS_OBJECT_TYPE_MISMATCH; + + *lm_pwd = obj->data.user->lm_pw; + return NT_STATUS_OK; +} + +NTSTATUS gums_set_user_lm_pwd(GUMS_OBJECT *obj, const DATA_BLOB lm_pwd) +{ + if (!obj || !lm_pwd || lm_pwd != LM_HASH_LEN) + return NT_STATUS_INVALID_PARAMETER; + + if (obj->type != GUMS_OBJ_NORMAL_USER) + return NT_STATUS_OBJECT_TYPE_MISMATCH; + + obj->data.user->lm_pwd = data_blob_talloc(obj->mem_ctx, lm_pwd.data, lm_pwd.lenght); + return NT_STATUS_OK; +} + +NTSTATUS gums_get_user_fullname(char **fullname, const GUMS_OBJECT *obj) +{ + if (!fullname || !obj) + return NT_STATUS_INVALID_PARAMETER; + + if (obj->type != GUMS_OBJ_NORMAL_USER) + return NT_STATUS_OBJECT_TYPE_MISMATCH; + + *fullname = obj->data.user->full_name; + 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->data.user->full_name = (char *)talloc_strdup(obj->mem_ctx, fullname); + if (!(obj->data.user->full_name)) return NT_STATUS_NO_MEMORY; + return NT_STATUS_OK; +} + +NTSTATUS gums_get_user_homedir(char **homedir, const GUMS_OBJECT *obj) +{ + if (!homedir || !obj) + return NT_STATUS_INVALID_PARAMETER; + + if (obj->type != GUMS_OBJ_NORMAL_USER) + return NT_STATUS_OBJECT_TYPE_MISMATCH; + + *homedir = obj->data.user->home_dir; + 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->data.user->home_dir = (char *)talloc_strdup(obj->mem_ctx, homedir); + if (!(obj->data.user->home_dir)) return NT_STATUS_NO_MEMORY; + return NT_STATUS_OK; +} + +NTSTATUS gums_get_user_dir_drive(char **dirdrive, const GUMS_OBJECT *obj) +{ + if (!dirdrive || !obj) + return NT_STATUS_INVALID_PARAMETER; + + if (obj->type != GUMS_OBJ_NORMAL_USER) + return NT_STATUS_OBJECT_TYPE_MISMATCH; + + *dirdrive = obj->data.user->dir_drive; + 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->data.user->dir_drive = (char *)talloc_strdup(obj->mem_ctx, dir_drive); + if (!(obj->data.user->dir_drive)) return NT_STATUS_NO_MEMORY; + return NT_STATUS_OK; +} + +NTSTATUS gums_get_user_logon_script(char **logon_script, const GUMS_OBJECT *obj) +{ + if (!logon_script || !obj) + return NT_STATUS_INVALID_PARAMETER; + + if (obj->type != GUMS_OBJ_NORMAL_USER) + return NT_STATUS_OBJECT_TYPE_MISMATCH; + + *logon_script = obj->data.user->logon_script; + 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->data.user->logon_script = (char *)talloc_strdup(obj->mem_ctx, logon_script); + if (!(obj->data.user->logon_script)) return NT_STATUS_NO_MEMORY; + return NT_STATUS_OK; +} + +NTSTATUS gums_get_user_profile_path(char **profile_path, const GUMS_OBJECT *obj) +{ + if (!profile_path || !obj) + return NT_STATUS_INVALID_PARAMETER; + + if (obj->type != GUMS_OBJ_NORMAL_USER) + return NT_STATUS_OBJECT_TYPE_MISMATCH; + + *profile_path = obj->data.user->profile_path; + 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->data.user->profile_path = (char *)talloc_strdup(obj->mem_ctx, profile_path); + if (!(obj->data.user->profile_path)) return NT_STATUS_NO_MEMORY; + return NT_STATUS_OK; +} + +NTSTATUS gums_get_user_workstations(char **workstations, const GUMS_OBJECT *obj) +{ + if (!workstations || !obj) + return NT_STATUS_INVALID_PARAMETER; + + if (obj->type != GUMS_OBJ_NORMAL_USER) + return NT_STATUS_OBJECT_TYPE_MISMATCH; + + *workstations = obj->data.user->workstations; + 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->data.user->workstations = (char *)talloc_strdup(obj->mem_ctx, workstations); + if (!(obj->data.user->workstations)) return NT_STATUS_NO_MEMORY; + return NT_STATUS_OK; +} + +NTSTATUS gums_get_user_unknown_str(char **unknown_str, const GUMS_OBJECT *obj) +{ + if (!unknown_str || !obj) + return NT_STATUS_INVALID_PARAMETER; + + if (obj->type != GUMS_OBJ_NORMAL_USER) + return NT_STATUS_OBJECT_TYPE_MISMATCH; + + *unknown_str = obj->data.user->unknown_str; + 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->data.user->unknown_str = (char *)talloc_strdup(obj->mem_ctx, unknown_str); + if (!(obj->data.user->unknown_str)) return NT_STATUS_NO_MEMORY; + return NT_STATUS_OK; +} + +NTSTATUS gums_get_user_munged_dial(char **munged_dial, const GUMS_OBJECT *obj) +{ + if (!munged_dial || !obj) + return NT_STATUS_INVALID_PARAMETER; + + if (obj->type != GUMS_OBJ_NORMAL_USER) + return NT_STATUS_OBJECT_TYPE_MISMATCH; + + *munged_dial = obj->data.user->munged_dial; + 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->data.user->munged_dial = (char *)talloc_strdup(obj->mem_ctx, munged_dial); + if (!(obj->data.user->munged_dial)) return NT_STATUS_NO_MEMORY; + return NT_STATUS_OK; +} + +NTSTATUS gums_get_user_logon_time(NTTIME *logon_time, const GUMS_OBJECT *obj) +{ + if (!logon_time || !obj) + return NT_STATUS_INVALID_PARAMETER; + + if (obj->type != GUMS_OBJ_NORMAL_USER) + return NT_STATUS_OBJECT_TYPE_MISMATCH; + + *logon_time = obj->data.user->logon_time; + 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->data.user->logon_time = logon_time; + return NT_STATUS_OK; +} + +NTSTATUS gums_get_user_logoff_time(NTTIME *logoff_time, const GUMS_OBJECT *obj) +{ + if (!logoff_time || !obj) + return NT_STATUS_INVALID_PARAMETER; + + if (obj->type != GUMS_OBJ_NORMAL_USER) + return NT_STATUS_OBJECT_TYPE_MISMATCH; + + *logoff_time = obj->data.user->logoff_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->data.user->logoff_time = logoff_time; + return NT_STATUS_OK; +} + +NTSTATUS gums_get_user_kickoff_time(NTTIME *kickoff_time, const GUMS_OBJECT *obj) +{ + if (!kickoff_time || !obj) + return NT_STATUS_INVALID_PARAMETER; + + if (obj->type != GUMS_OBJ_NORMAL_USER) + return NT_STATUS_OBJECT_TYPE_MISMATCH; + + *kickoff_time = obj->data.user->kickoff_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->data.user->kickoff_time = kickoff_time; + return NT_STATUS_OK; +} + +NTSTATUS gums_get_user_pass_last_set_time(NTTIME *pass_last_set_time, const GUMS_OBJECT *obj) +{ + if (!pass_last_set_time || !obj) + return NT_STATUS_INVALID_PARAMETER; + + if (obj->type != GUMS_OBJ_NORMAL_USER) + return NT_STATUS_OBJECT_TYPE_MISMATCH; + + *pass_last_set_time = obj->data.user->pass_last_set_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->data.user->pass_last_set_time = pass_last_set_time; + return NT_STATUS_OK; +} + +NTSTATUS gums_get_user_pass_can_change_time(NTTIME *pass_can_change_time, const GUMS_OBJECT *obj) +{ + if (!pass_can_change_time || !obj) + return NT_STATUS_INVALID_PARAMETER; + + if (obj->type != GUMS_OBJ_NORMAL_USER) + return NT_STATUS_OBJECT_TYPE_MISMATCH; + + *pass_can_change_time = obj->data.user->pass_can_change_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->data.user->pass_can_change_time = pass_can_change_time; + return NT_STATUS_OK; +} + +NTSTATUS gums_get_user_pass_must_change_time(NTTIME *pass_must_change_time, const GUMS_OBJECT *obj) +{ + if (!pass_must_change_time || !obj) + return NT_STATUS_INVALID_PARAMETER; + + if (obj->type != GUMS_OBJ_NORMAL_USER) + return NT_STATUS_OBJECT_TYPE_MISMATCH; + + *pass_must_change_time = obj->data-user->pass_must_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->data.user->pass_must_change_time = pass_must_change_time; + return NT_STATUS_OK; +} + +NTSTATUS gums_get_user_logon_divs(uint16 *logon_divs, const GUMS_OBJECT *obj) +{ + if (!logon_divs || !obj) + return NT_STATUS_INVALID_PARAMETER; + + if (obj->type != GUMS_OBJ_NORMAL_USER) + return NT_STATUS_OBJECT_TYPE_MISMATCH; + + *logon_divs = obj->data.user->logon_divs; + return NT_STATUS_OK; +} + +NTSTATUS gums_set_user_logon_divs(GUMS_OBJECT *obj, uint16 logon_divs) +{ + if (!obj || !logon_divs) + return NT_STATUS_INVALID_PARAMETER; + + if (obj->type != GUMS_OBJ_NORMAL_USER) + return NT_STATUS_OBJECT_TYPE_MISMATCH; + + obj->data.user->logon_divs = logon_divs; + return NT_STATUS_OK; +} + +NTSTATUS gums_get_user_hours_len(uint32 *hours_len, const GUMS_OBJECT *obj) +{ + if (!hours_len || !obj) + return NT_STATUS_INVALID_PARAMETER; + + if (obj->type != GUMS_OBJ_NORMAL_USER) + return NT_STATUS_OBJECT_TYPE_MISMATCH; + + *hours_len = obj->data.user->hours_len; + return NT_STATUS_OK; +} + +NTSTATUS gums_set_user_hours_len(GUMS_OBJECT *obj, uint32 hours_len) +{ + if (!obj) + return NT_STATUS_INVALID_PARAMETER; + + if (obj->type != GUMS_OBJ_NORMAL_USER) + return NT_STATUS_OBJECT_TYPE_MISMATCH; + + obj->data.user->hours_len = hours_len; + return NT_STATUS_OK; +} + +NTSTATUS gums_get_user_hours(uint8 **hours, const GUMS_OBJECT *obj) +{ + if (!hours || !obj) + return NT_STATUS_INVALID_PARAMETER; + + if (obj->type != GUMS_OBJ_NORMAL_USER) + return NT_STATUS_OBJECT_TYPE_MISMATCH; + + *hours = obj->data.user->hours; + return NT_STATUS_OK; +} + +/* WARNING: always set hours_len before hours */ +NTSTATUS gums_set_user_hours(GUMS_OBJECT *obj, const uint8 *hours) +{ + if (!obj || !hours) + return NT_STATUS_INVALID_PARAMETER; + + if (obj->type != GUMS_OBJ_NORMAL_USER) + return NT_STATUS_OBJECT_TYPE_MISMATCH; + + if (obj->data.user->hours_len == 0) + DEBUG(10, ("gums_set_user_hours: Warning, hours_len is zero!\n")); + + obj->data.user->hours = (uint8 *)talloc_memdup(obj->mem_ctx, hours, obj->data.user->hours_len); + if (!(obj->data.user->hours) & (obj->data.user->hours_len != 0)) return NT_STATUS_NO_MEMORY; + return NT_STATUS_OK; +} + +NTSTATUS gums_get_user_unknown_3(uint32 *unknown_3, const GUMS_OBJECT *obj) +{ + if (!unknown_3 || !obj) + return NT_STATUS_INVALID_PARAMETER; + + if (obj->type != GUMS_OBJ_NORMAL_USER) + return NT_STATUS_OBJECT_TYPE_MISMATCH; + + *unknown_3 = obj->data.user->unknown_3; + 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->data.user->unknown_3 = unknown_3; + return NT_STATUS_OK; +} + +NTSTATUS gums_get_user_unknown_5(uint32 *unknown_5, const GUMS_OBJECT *obj) +{ + if (!unknown_5 || !obj) + return NT_STATUS_INVALID_PARAMETER; + + if (obj->type != GUMS_OBJ_NORMAL_USER) + return NT_STATUS_OBJECT_TYPE_MISMATCH; + + *unknown_5 = obj->data.user->unknown_5; + return NT_STATUS_OK; +} + +NTSTATUS gums_set_user_unknown_5(GUMS_OBJECT *obj, uint32 unknown_5) +{ + if (!obj) + return NT_STATUS_INVALID_PARAMETER; + + if (obj->type != GUMS_OBJ_NORMAL_USER) + return NT_STATUS_OBJECT_TYPE_MISMATCH; + + obj->data.user->unknown_5 = unknown_5; + return NT_STATUS_OK; +} + +NTSTATUS gums_get_user_unknown_6(uint32 *unknown_6, const GUMS_OBJECT *obj) +{ + if (!unknown_6 || !obj) + return NT_STATUS_INVALID_PARAMETER; + + if (obj->type != GUMS_OBJ_NORMAL_USER) + return NT_STATUS_OBJECT_TYPE_MISMATCH; + + *unknown_6 = obj->data.user->unknown_6; + 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->data.user->unknown_6 = unknown_6; + return NT_STATUS_OK; +} + +/* Group specific functions */ + +NTSTATUS gums_get_group_members(uint32 *count, DOM_SID **members, const GUMS_OBJECT *obj) +{ + if (!count || !members || !obj) + return NT_STATUS_INVALID_PARAMETER; + + if (obj->type != GUMS_OBJ_GROUP && + obj->type != GUMS_OBJ_ALIAS) + return NT_STATUS_OBJECT_TYPE_MISMATCH; + + *count = obj->data.group->count; + *members = obj->data.group->members; + return NT_STATUS_OK; +} + +NTSTATUS gums_set_group_members(GUMS_OBJECT *obj, uint32 count, DOM_SID **members) +{ + uint32 n; + + if (!obj || !members || !members) + return NT_STATUS_INVALID_PARAMETER; + + if (obj->type != GUMS_OBJ_GROUP && + obj->type != GUMS_OBJ_ALIAS) + return NT_STATUS_OBJECT_TYPE_MISMATCH; + + obj->data.group->count = count; + n = 0; + do { + obj->data.group->members[n] = dup_sec_desc(obj->mem_ctx, members[n]); + if (!(obj->data.group->members[n])) return NT_STATUS_NO_MEMORY; + n++; + } while (n < count); + return NT_STATUS_OK; +} + +/* data_store set functions */ + +NTSTATUS gums_create_commit_set(GUMS_COMMIT_SET **com_set, TALLOC_CTX *ctx, DOM_SID *sid, uint32 type) +{ + TALLOC_CTX *mem_ctx; + GUMS_COMMIT_SET *set; + + mem_ctx = talloc_init("commit_set"); + if (mem_ctx == NULL) + return NT_STATUS_NO_MEMORY; + set = (GUMS_COMMIT_SET *)talloc(mem_ctx, sizeof(GUMS_COMMIT_SET)); + if (set == NULL) { + talloc_destroy(mem_ctx); + return NT_STATUS_NO_MEMORY; + } + + set->mem_ctx = mem_ctx; + set->type = type; + sid_copy(&(set->sid), sid); + set->count = 0; + set->data = NULL; + *com_set = set; + + return NT_STATUS_OK; +} + +NTSTATUS gums_cs_set_sec_desc(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, SEC_DESC *sec_desc) +{ + GUMS_DATA_SET *data_set; + SEC_DESC *new_sec_desc; + + if (!mem_ctx || !com_set || !sec_desc) + return NT_STATUS_INVALID_PARAMETER; + + com_set->count = com_set->count + 1; + if (com_set->count == 1) { /* first data set */ + data_set = (GUMS_DATA_SET *)talloc(mem_ctx, sizeof(GUMS_DATA_SET)); + } else { + data_set = (GUMS_DATA_SET *)talloc_realloc(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; + data_set = &((com_set->data)[com_set->count - 1]); + + data_set->type = GUMS_SET_SEC_DESC; + new_sec_desc = dup_sec_desc(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(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, LUID_ATTR priv) +{ + GUMS_DATA_SET *data_set; + LUID_ATTR *new_priv; + + if (!mem_ctx || !com_set) + return NT_STATUS_INVALID_PARAMETER; + + com_set->count = com_set->count + 1; + if (com_set->count == 1) { /* first data set */ + data_set = (GUMS_DATA_SET *)talloc(mem_ctx, sizeof(GUMS_DATA_SET)); + } else { + data_set = (GUMS_DATA_SET *)talloc_realloc(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; + data_set = &((com_set->data)[com_set->count - 1]); + + data_set->type = GUMS_ADD_PRIVILEGE; + if (NT_STATUS_IS_ERR(dupalloc_luid_attr(mem_ctx, &new_priv, priv))) + return NT_STATUS_NO_MEMORY; + + (SEC_DESC *)(data_set->data) = new_priv; + + return NT_STATUS_OK; +} + +NTSTATUS gums_cs_del_privilege(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, LUID_ATTR priv) +{ + GUMS_DATA_SET *data_set; + LUID_ATTR *new_priv; + + if (!mem_ctx || !com_set) + return NT_STATUS_INVALID_PARAMETER; + + com_set->count = com_set->count + 1; + if (com_set->count == 1) { /* first data set */ + data_set = (GUMS_DATA_SET *)talloc(mem_ctx, sizeof(GUMS_DATA_SET)); + } else { + data_set = (GUMS_DATA_SET *)talloc_realloc(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; + data_set = &((com_set->data)[com_set->count - 1]); + + data_set->type = GUMS_DEL_PRIVILEGE; + if (NT_STATUS_IS_ERR(dupalloc_luid_attr(mem_ctx, &new_priv, priv))) + return NT_STATUS_NO_MEMORY; + + (SEC_DESC *)(data_set->data) = new_priv; + + return NT_STATUS_OK; +} + +NTSTATUS gums_cs_set_privilege_set(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, PRIVILEGE_SET *priv_set) +{ + GUMS_DATA_SET *data_set; + PRIVILEGE_SET *new_priv_set; + + if (!mem_ctx || !com_set || !priv_set) + return NT_STATUS_INVALID_PARAMETER; + + com_set->count = com_set->count + 1; + if (com_set->count == 1) { /* first data set */ + data_set = (GUMS_DATA_SET *)talloc(mem_ctx, sizeof(GUMS_DATA_SET)); + } else { + data_set = (GUMS_DATA_SET *)talloc_realloc(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; + data_set = &((com_set->data)[com_set->count - 1]); + + data_set->type = GUMS_SET_SEC_DESC; + if (NT_STATUS_IS_ERR(dup_priv_set(&new_priv_set, mem_ctx, priv_set))) + return NT_STATUS_NO_MEMORY; + + (SEC_DESC *)(data_set->data) = new_priv_set; + + return NT_STATUS_OK; +} + +NTSTATUS gums_cs_set_string(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, uint32 type, char *str) +{ + GUMS_DATA_SET *data_set; + char *new_str; + + if (!mem_ctx || !com_set || !str || type < GUMS_SET_NAME || type > GUMS_SET_MUNGED_DIAL) + return NT_STATUS_INVALID_PARAMETER; + + com_set->count = com_set->count + 1; + if (com_set->count == 1) { /* first data set */ + data_set = (GUMS_DATA_SET *)talloc(mem_ctx, sizeof(GUMS_DATA_SET)); + } else { + data_set = (GUMS_DATA_SET *)talloc_realloc(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; + data_set = &((com_set->data)[com_set->count - 1]); + + data_set->type = type; + new_str = talloc_strdup(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(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, char *name) +{ + return gums_set_string(mem_ctx, com_set, GUMS_SET_NAME, name); +} + +NTSTATUS gums_cs_set_description(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, char *desc) +{ + return gums_set_string(mem_ctx, com_set, GUMS_SET_DESCRIPTION, desc); +} + +NTSTATUS gums_cs_set_full_name(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, char *full_name) +{ + if (com_set->type != GUMS_OBJ_NORMAL_USER) + return NT_STATUS_INVALID_PARAMETER; + + return gums_set_string(mem_ctx, com_set, GUMS_SET_NAME, full_name); +} + +NTSTATUS gums_cs_set_home_directory(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, char *home_dir) +{ + if (com_set->type != GUMS_OBJ_NORMAL_USER) + return NT_STATUS_INVALID_PARAMETER; + + return gums_set_string(mem_ctx, com_set, GUMS_SET_NAME, home_dir); +} + +NTSTATUS gums_cs_set_drive(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, char *drive) +{ + if (com_set->type != GUMS_OBJ_NORMAL_USER) + return NT_STATUS_INVALID_PARAMETER; + + return gums_set_string(mem_ctx, com_set, GUMS_SET_NAME, drive); +} + +NTSTATUS gums_cs_set_logon_script(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, char *logon_script) +{ + if (com_set->type != GUMS_OBJ_NORMAL_USER) + return NT_STATUS_INVALID_PARAMETER; + + return gums_set_string(mem_ctx, com_set, GUMS_SET_NAME, logon_script); +} + +NTSTATUS gums_cs_set_profile_path(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, char *prof_path) +{ + if (com_set->type != GUMS_OBJ_NORMAL_USER) + return NT_STATUS_INVALID_PARAMETER; + + return gums_set_string(mem_ctx, com_set, GUMS_SET_NAME, prof_path); +} + +NTSTATUS gums_cs_set_workstations(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, char *wks) +{ + if (com_set->type != GUMS_OBJ_NORMAL_USER) + return NT_STATUS_INVALID_PARAMETER; + + return gums_set_string(mem_ctx, com_set, GUMS_SET_NAME, wks); +} + +NTSTATUS gums_cs_set_unknown_string(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, char *unkn_str) +{ + if (com_set->type != GUMS_OBJ_NORMAL_USER) + return NT_STATUS_INVALID_PARAMETER; + + return gums_set_string(mem_ctx, com_set, GUMS_SET_NAME, unkn_str); +} + +NTSTATUS gums_cs_set_munged_dial(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, char *munged_dial) +{ + if (com_set->type != GUMS_OBJ_NORMAL_USER) + return NT_STATUS_INVALID_PARAMETER; + + return gums_set_string(mem_ctx, com_set, GUMS_SET_NAME, munged_dial); +} + +NTSTATUS gums_cs_set_nttime(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, uint32 type, NTTIME *nttime) +{ + GUMS_DATA_SET *data_set; + NTTIME *new_time; + + if (!mem_ctx || !com_set || !nttime || type < GUMS_SET_LOGON_TIME || type > GUMS_SET_PASS_MUST_CHANGE_TIME) + return NT_STATUS_INVALID_PARAMETER; + + com_set->count = com_set->count + 1; + if (com_set->count == 1) { /* first data set */ + data_set = (GUMS_DATA_SET *)talloc(mem_ctx, sizeof(GUMS_DATA_SET)); + } else { + data_set = (GUMS_DATA_SET *)talloc_realloc(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; + data_set = &((com_set->data)[com_set->count - 1]); + + data_set->type = type; + new_time = talloc(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(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, NTTIME *logon_time) +{ + if (com_set->type != GUMS_OBJ_NORMAL_USER) + return NT_STATUS_INVALID_PARAMETER; + + return gums_set_nttime(mem_ctx, com_set, GUMS_SET_LOGON_TIME, logon_time); +} + +NTSTATUS gums_cs_set_logoff_time(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, NTTIME *logoff_time) +{ + if (com_set->type != GUMS_OBJ_NORMAL_USER) + return NT_STATUS_INVALID_PARAMETER; + + return gums_set_nttime(mem_ctx, com_set, GUMS_SET_LOGOFF_TIME, logoff_time); +} + +NTSTATUS gums_cs_set_kickoff_time(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, NTTIME *kickoff_time) +{ + if (com_set->type != GUMS_OBJ_NORMAL_USER) + return NT_STATUS_INVALID_PARAMETER; + + return gums_set_nttime(mem_ctx, com_set, GUMS_SET_KICKOFF_TIME, kickoff_time); +} + +NTSTATUS gums_cs_set_pass_last_set_time(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, NTTIME *pls_time) +{ + if (com_set->type != GUMS_OBJ_NORMAL_USER) + return NT_STATUS_INVALID_PARAMETER; + + return gums_set_nttime(mem_ctx, com_set, GUMS_SET_LOGON_TIME, pls_time); +} + +NTSTATUS gums_cs_set_pass_can_change_time(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, NTTIME *pcc_time) +{ + if (com_set->type != GUMS_OBJ_NORMAL_USER) + return NT_STATUS_INVALID_PARAMETER; + + return gums_set_nttime(mem_ctx, com_set, GUMS_SET_LOGON_TIME, pcc_time); +} + +NTSTATUS gums_cs_set_pass_must_change_time(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, NTTIME *pmc_time) +{ + if (com_set->type != GUMS_OBJ_NORMAL_USER) + return NT_STATUS_INVALID_PARAMETER; + + return gums_set_nttime(mem_ctx, com_set, GUMS_SET_LOGON_TIME, pmc_time); +} + +NTSTATUS gums_cs_add_sids_to_group(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, const DOM_SID **sids, const uint32 count) +{ + GUMS_DATA_SET *data_set; + DOM_SID **new_sids; + int i; + + if (!mem_ctx || !com_set || !sids) + return NT_STATUS_INVALID_PARAMETER; + + com_set->count = com_set->count + 1; + if (com_set->count == 1) { /* first data set */ + data_set = (GUMS_DATA_SET *)talloc(mem_ctx, sizeof(GUMS_DATA_SET)); + } else { + data_set = (GUMS_DATA_SET *)talloc_realloc(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; + data_set = &((com_set->data)[com_set->count - 1]); + + data_set->type = GUMS_ADD_SID_LIST; + new_sids = (DOM_SID **)talloc(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(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(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, const DOM_SID **sids, const uint32 count) +{ + if (!mem_ctx || !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_add_sids_to_group(mem_ctx, com_set, sids, count); +} + +NTSTATUS gums_cs_add_groups_to_group(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, const DOM_SID **sids, const uint32 count) +{ + if (!mem_ctx || !com_set || !sids) + return NT_STATUS_INVALID_PARAMETER; + if (com_set->type != GUMS_OBJ_ALIAS) + return NT_STATUS_INVALID_PARAMETER; + + return gums_add_sids_to_group(mem_ctx, com_set, sids, count); +} + +NTSTATUS gums_cs_del_sids_from_group(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, const DOM_SID **sids, const uint32 count) +{ + GUMS_DATA_SET *data_set; + DOM_SID **new_sids; + int i; + + if (!mem_ctx || !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; + + com_set->count = com_set->count + 1; + if (com_set->count == 1) { /* first data set */ + data_set = (GUMS_DATA_SET *)talloc(mem_ctx, sizeof(GUMS_DATA_SET)); + } else { + data_set = (GUMS_DATA_SET *)talloc_realloc(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; + data_set = &((com_set->data)[com_set->count - 1]); + + data_set->type = GUMS_DEL_SID_LIST; + new_sids = (DOM_SID **)talloc(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(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(TALLOC_CTX *mem_ctx, GUMS_COMMIT_SET *com_set, const DOM_SID **sids, const uint32 count) +{ + GUMS_DATA_SET *data_set; + DOM_SID **new_sids; + int i; + + if (!mem_ctx || !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; + + com_set->count = com_set->count + 1; + if (com_set->count == 1) { /* first data set */ + data_set = (GUMS_DATA_SET *)talloc(mem_ctx, sizeof(GUMS_DATA_SET)); + } else { + data_set = (GUMS_DATA_SET *)talloc_realloc(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; + data_set = &((com_set->data)[com_set->count - 1]); + + data_set->type = GUMS_SET_SID_LIST; + new_sids = (DOM_SID **)talloc(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(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) +{ + return gums_storage->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/source4/sam/gums_helper.c b/source4/sam/gums_helper.c new file mode 100644 index 0000000000..8526a2f1cc --- /dev/null +++ b/source4/sam/gums_helper.c @@ -0,0 +1,607 @@ +/* + 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 GUMS_FUNCTIONS *gums_storage; + +extern DOM_SID global_sid_World; +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(str1, str2, err, label) do { if (NT_STATUS_IS_ERR(err)) { DEBUG(0, ("%s: %s failed!\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. + ****************************************************************************/ + +/* +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; +} +*/ + +/**************************************************************************** + duplicate alloc luid_attr + ****************************************************************************/ +NTSTATUS dupalloc_luid_attr(TALLOC_CTX *ctx, LUID_ATTR **new_la, LUID_ATTR old_la) +{ + *new_la = (LUID_ATTR *)talloc(ctx, sizeof(LUID_ATTR)); + if (*new_la == NULL) { + DEBUG(0,("dupalloc_luid_attr: could not Alloc memory to duplicate LUID_ATTR\n")); + return NT_STATUS_NO_MEMORY; + } + + (*new_la)->luid.high = old_la.luid.high; + (*new_la)->luid.low = old_la.luid.low; + (*new_la)->attr = old_la.attr; + + return NT_STATUS_OK; +} + +/**************************************************************************** + initialise a privilege list + ****************************************************************************/ +void init_privilege(PRIVILEGE_SET *priv_set) +{ + priv_set->count=0; + priv_set->control=0; + priv_set->set=NULL; +} + +/**************************************************************************** + add a privilege to a privilege array + ****************************************************************************/ +NTSTATUS add_privilege(PRIVILEGE_SET *priv_set, TALLOC_CTX *ctx, LUID_ATTR set) +{ + LUID_ATTR *new_set; + + /* check if the privilege is not already in the list */ + if (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(ctx, priv_set->set, (priv_set->count+1)*(sizeof(LUID_ATTR))); + if (new_set==NULL) { + DEBUG(0,("add_privilege: could not Realloc memory to add a new privilege\n")); + return NT_STATUS_NO_MEMORY; + } + + 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; + + return NT_STATUS_OK; +} + +/**************************************************************************** + add all the privileges to a privilege array + ****************************************************************************/ +NTSTATUS add_all_privilege(PRIVILEGE_SET *priv_set, TALLOC_CTX *ctx) +{ + NTSTATUS result = NT_STATUS_OK; + LUID_ATTR set; + + set.attr=0; + set.luid.high=0; + + set.luid.low=SE_PRIV_ADD_USERS; + result = add_privilege(priv_set, ctx, set); + NTSTATUS_CHECK("add_all_privilege", "add_privilege", result, done); + + set.luid.low=SE_PRIV_ADD_MACHINES; + result = add_privilege(priv_set, ctx, set); + NTSTATUS_CHECK("add_all_privilege", "add_privilege", result, done); + + set.luid.low=SE_PRIV_PRINT_OPERATOR; + result = add_privilege(priv_set, ctx, set); + NTSTATUS_CHECK("add_all_privilege", "add_privilege", result, done); + +done: + return result; +} + +/**************************************************************************** + check if the privilege list is empty + ****************************************************************************/ +BOOL check_empty_privilege(PRIVILEGE_SET *priv_set) +{ + return (priv_set->count == 0); +} + +/**************************************************************************** + check if the privilege is in the privilege list + ****************************************************************************/ +BOOL check_priv_in_privilege(PRIVILEGE_SET *priv_set, LUID_ATTR set) +{ + int i; + + /* if the list is empty, obviously we can't have it */ + if (check_empty_privilege(priv_set)) + return False; + + for (i=0; icount; 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 True; + } + + return False; +} + +/**************************************************************************** + remove a privilege from a privilege array + ****************************************************************************/ +NTSTATUS remove_privilege(PRIVILEGE_SET *priv_set, TALLOC_CTX *ctx, LUID_ATTR set) +{ + LUID_ATTR *new_set; + LUID_ATTR *old_set; + int i,j; + + /* check if the privilege is in the list */ + if (!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) { + init_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(ctx, (priv_set->count - 1) * (sizeof(LUID_ATTR))); + if (new_set==NULL) { + DEBUG(0,("remove_privilege: could not malloc memory for new privilege list\n")); + return NT_STATUS_NO_MEMORY; + } + + for (i=0, j=0; icount; 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; + + return NT_STATUS_OK; +} + +/**************************************************************************** + duplicates a privilege array + ****************************************************************************/ +NTSTATUS dup_priv_set(PRIVILEGE_SET **new_priv_set, TALLOC_CTX *mem_ctx, PRIVILEGE_SET *priv_set) +{ + LUID_ATTR *new_set; + LUID_ATTR *old_set; + int i; + + *new_priv_set = (PRIVILEGE_SET *)talloc(mem_ctx, sizeof(PRIVILEGE_SET)); + init_privilege(*new_priv_set); + + /* 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(mem_ctx, (priv_set->count - 1) * (sizeof(LUID_ATTR))); + if (new_set==NULL) { + DEBUG(0,("remove_privilege: could not malloc memory for new privilege list\n")); + return NT_STATUS_NO_MEMORY; + } + + 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; + + return NT_STATUS_OK; +} + +#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, 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_init_builtin_groups(void) +{ + NTSTATUS result; + GUMS_OBJECT g_obj; + GUMS_GROUP *g_grp; + GUMS_PRIVILEGE g_priv; + + /* Build the well known Builtin Local Groups */ + g_obj.type = GUMS_OBJ_GROUP; + g_obj.version = 1; + g_obj.seq_num = 0; + g_obj.mem_ctx = talloc_init("gums_init_backend_acct"); + if (g_obj.mem_ctx == NULL) { + DEBUG(0, ("gums_init_backend: Out of Memory!\n")); + return NT_STATUS_NO_MEMORY; + } + + /* Administrators */ + + /* alloc group structure */ + g_obj.data = (void *)talloc(g_obj.mem_ctx, sizeof(GUMS_OBJ_GROUP)); + ALLOC_CHECK("gums_init_backend", g_obj.data, result, done); + + /* make admins sid */ + g_grp = (GUMS_GROUP *)g_obj.data; + sid_copy(g_obj.sid, &global_sid_Builtin_Administrators); + + /* make security descriptor */ + result = create_builtin_alias_default_sec_desc(&(g_obj.sec_desc), g_obj.mem_ctx); + NTSTATUS_CHECK("gums_init_backend", "create_builtin_alias_default_sec_desc", result, done); + + /* make privilege set */ + /* From BDC join trace: + SeSecurityPrivilege + SeBackupPrivilege + SeRestorePrivilege + SeSystemtimePrivilege + SeShutdownPrivilege + SeRemoteShutdownPrivilege + SeTakeOwnershipPrivilege + SeDebugPrivilege + SeSystemEnvironmentPrivilege + SeSystemProfilePrivilege + SeProfileSingleProcessPrivilege + SeIncreaseBasePriorityPrivilege + SeLocalDriverPrivilege + SeCreatePagefilePrivilege + SeIncreaseQuotaPrivilege + */ + + /* set name */ + g_obj.name = talloc_strdup(g_obj.mem_ctx, "Administrators"); + ALLOC_CHECK("gums_init_backend", g_obj.name, result, done); + + /* set description */ + g_obj.description = talloc_strdup(g_obj.mem_ctx, "Members can fully administer the computer/domain"); + ALLOC_CHECK("gums_init_backend", g_obj.description, result, done); + + /* numebr of group members */ + g_grp->count = 0; + g_grp->members = NULL; + + /* store Administrators group */ + result = gums_storage->set_object(&g_obj); + + /* Power Users */ + /* Domain Controllers Does NOT have power Users */ + + sid_copy(g_obj.sid, &global_sid_Builtin_Power_Users); + + /* make privilege set */ + /* SE_PRIV_??? */ + + /* set name */ + g_obj.name = talloc_strdup(g_obj.mem_ctx, "Power Users"); + ALLOC_CHECK("gums_init_backend", g_obj.name, result, done); + + /* set description */ +/* > */ g_obj.description = talloc_strdup(g_obj.mem_ctx, "Power Users"); + ALLOC_CHECK("gums_init_backend", g_obj.description, result, done); + + /* store Power Users group */ + result = gums_storage->set_object(&g_obj); + + /* Account Operators */ + + sid_copy(g_obj.sid, &global_sid_Builtin_Account_Operators); + + /* make privilege set */ + /* From BDC join trace: + SeShutdownPrivilege + */ + + /* set name */ + g_obj.name = talloc_strdup(g_obj.mem_ctx, "Account Operators"); + ALLOC_CHECK("gums_init_backend", g_obj.name, result, done); + + /* set description */ + g_obj.description = talloc_strdup(g_obj.mem_ctx, "Members can administer domain user and group accounts"); + ALLOC_CHECK("gums_init_backend", g_obj.description, result, done); + + /* store Account Operators group */ + result = gums_storage->set_object(&g_obj); + + /* Server Operators */ + + sid_copy(g_obj.sid, &global_sid_Builtin_Server_Operators); + + /* make privilege set */ + /* From BDC join trace: + SeBackupPrivilege + SeRestorePrivilege + SeSystemtimePrivilege + SeShutdownPrivilege + SeRemoteShutdownPrivilege + */ + + /* set name */ + g_obj.name = talloc_strdup(g_obj.mem_ctx, "Server Operators"); + ALLOC_CHECK("gums_init_backend", g_obj.name, result, done); + + /* set description */ + g_obj.description = talloc_strdup(g_obj.mem_ctx, "Members can administer domain servers"); + ALLOC_CHECK("gums_init_backend", g_obj.description, result, done); + + /* store Server Operators group */ + result = gums_storage->set_object(&g_obj); + + /* Print Operators */ + + sid_copy(g_obj.sid, &global_sid_Builtin_Print_Operators); + + /* make privilege set */ + /* From BDC join trace: + SeShutdownPrivilege + */ + + /* set name */ + g_obj.name = talloc_strdup(g_obj.mem_ctx, "Print Operators"); + ALLOC_CHECK("gums_init_backend", g_obj.name, result, done); + + /* set description */ + g_obj.description = talloc_strdup(g_obj.mem_ctx, "Members can administer domain printers"); + ALLOC_CHECK("gums_init_backend", g_obj.description, result, done); + + /* store Print Operators group */ + result = gums_storage->set_object(&g_obj); + + /* Backup Operators */ + + sid_copy(g_obj.sid, &global_sid_Builtin_Backup_Operators); + + /* make privilege set */ + /* From BDC join trace: + SeBackupPrivilege + SeRestorePrivilege + SeShutdownPrivilege + */ + + /* set name */ + g_obj.name = talloc_strdup(g_obj.mem_ctx, "Backup Operators"); + ALLOC_CHECK("gums_init_backend", g_obj.name, result, done); + + /* set description */ + g_obj.description = talloc_strdup(g_obj.mem_ctx, "Members can bypass file security to backup files"); + ALLOC_CHECK("gums_init_backend", g_obj.description, result, done); + + /* store Backup Operators group */ + result = gums_storage->set_object(&g_obj); + + /* Replicator */ + + sid_copy(g_obj.sid, &global_sid_Builtin_Replicator); + + /* make privilege set */ + /* From BDC join trace: + SeBackupPrivilege + SeRestorePrivilege + SeShutdownPrivilege + */ + + /* set name */ + g_obj.name = talloc_strdup(g_obj.mem_ctx, "Replicator"); + ALLOC_CHECK("gums_init_backend", g_obj.name, result, done); + + /* set description */ + g_obj.description = talloc_strdup(g_obj.mem_ctx, "Supports file replication in a domain"); + ALLOC_CHECK("gums_init_backend", g_obj.description, result, done); + + /* store Replicator group */ + result = gums_storage->set_object(&g_obj); + + /* Users */ + + sid_copy(g_obj.sid, &global_sid_Builtin_Users); + + /* add ACE to sec dsec dacl */ + sec_desc_add_ace_to_dacl(g_obj.sec_desc, g_obj.mem_ctx, &global_sid_Builtin_Account_Operators, ALIAS_DEFAULT_DACL_SA_RIGHTS); + sec_desc_add_ace_to_dacl(g_obj.sec_desc, g_obj.mem_ctx, &global_sid_Builtin_Power_Users, ALIAS_DEFAULT_DACL_SA_RIGHTS); + + /* set name */ + g_obj.name = talloc_strdup(g_obj.mem_ctx, "Users"); + ALLOC_CHECK("gums_init_backend", g_obj.name, result, done); + + /* set description */ + g_obj.description = talloc_strdup(g_obj.mem_ctx, "Ordinary users"); + ALLOC_CHECK("gums_init_backend", g_obj.description, result, done); + + /* store Users group */ + result = gums_storage->set_object(&g_obj); + + /* Guests */ + + sid_copy(g_obj.sid, &global_sid_Builtin_Guests); + + /* set name */ + g_obj.name = talloc_strdup(g_obj.mem_ctx, "Guests"); + ALLOC_CHECK("gums_init_backend", g_obj.name, result, done); + + /* set description */ + g_obj.description = talloc_strdup(g_obj.mem_ctx, "Users granted guest access to the computer/domain"); + ALLOC_CHECK("gums_init_backend", g_obj.description, result, done); + + /* store Guests group */ + result = gums_storage->set_object(&g_obj); + + /* set default privileges */ + g_priv.type = GUMS_OBJ_GROUP; + g_priv.version = 1; + g_priv.seq_num = 0; + g_priv.mem_ctx = talloc_init("gums_init_backend_priv"); + if (g_priv.mem_ctx == NULL) { + DEBUG(0, ("gums_init_backend: Out of Memory!\n")); + return NT_STATUS_NO_MEMORY; + } + + + +done: + talloc_destroy(g_obj.mem_ctx); + talloc_destroy(g_priv.mem_ctx); + return result; +} + diff --git a/source4/sam/interface.c b/source4/sam/interface.c new file mode 100644 index 0000000000..51ae561999 --- /dev/null +++ b/source4/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/source4/sam/sam_ads.c b/source4/sam/sam_ads.c new file mode 100755 index 0000000000..13e0369004 --- /dev/null +++ b/source4/sam/sam_ads.c @@ -0,0 +1,1378 @@ +/* + Unix SMB/CIFS implementation. + Active Directory SAM backend, for simulate a W2K DC in mixed mode. + + Copyright (C) Stefan (metze) Metzmacher 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" + + +#ifdef HAVE_LDAP + +static int sam_ads_debug_level = DBGC_SAM; + +#undef DBGC_CLASS +#define DBGC_CLASS sam_ads_debug_level + +#ifndef FIXME +#define FIXME( body ) { DEBUG(0,("FIXME: "));\ + DEBUGADD(0,(body));} +#endif + +#define ADS_STATUS_OK ADS_ERROR(0) +#define ADS_STATUS_UNSUCCESSFUL ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL) +#define ADS_STATUS_NOT_IMPLEMENTED ADS_ERROR_NT(NT_STATUS_NOT_IMPLEMENTED) + + +#define ADS_SUBTREE_BUILTIN "CN=Builtin," +#define ADS_SUBTREE_COMPUTERS "CN=Computers," +#define ADS_SUBTREE_DC "CN=Domain Controllers," +#define ADS_SUBTREE_USERS "CN=Users," +#define ADS_ROOT_TREE "" +/* Here are private module structs and functions */ + +typedef struct sam_ads_privates { + ADS_STRUCT *ads_struct; + TALLOC_CTX *mem_ctx; + BOOL bind_plaintext; + char *ads_bind_dn; + char *ads_bind_pw; + char *ldap_uri; + /* did we need something more? */ +}SAM_ADS_PRIVATES; + + +/* get only these LDAP attributes, witch we really need for an account */ +const char *account_attrs[] = { "objectSid", + "objectGUID", + "sAMAccountType", + "sAMAcountName", + "userPrincipalName", + "accountExpires", + "badPasswordTime", + "badPwdCount", + "lastLogoff", + "lastLogon", + "userWorkstations", + "dBCSPwd", + "unicodePwd", + "pwdLastSet", + "userAccountControl", + "profilePath", + "homeDrive", + "scriptPath", + "homeDirectory", + "cn", + "primaryGroupID",/* 513 */ + "nsNPAllowDialIn",/* TRUE */ + "userParameters",/* Dial Back number ...*/ + "codePage",/* 0 */ + "countryCode",/* 0 */ + "adminCount",/* 1 or 0 */ + "logonCount",/* 0 */ + "managedObjects", + "memberOf",/* dn */ + "instanceType",/* 4 */ + "name", /* sync with cn */ + "description", + /* "nTSecurityDescriptor", */ + NULL}; + +/* get only these LDAP attributes, witch we really need for a group */ +const char *group_attrs[] = {"objectSid", + /* "objectGUID", */ + "sAMAccountType", + "sAMAcountName", + "groupType", + /* "member", */ + "description", + "name", /* sync with cn */ + /* "nTSecurityDescriptor", */ + NULL}; + + +/*************************************************** + return our ads connection. We keep the connection + open to make things faster +****************************************************/ +static ADS_STATUS sam_ads_cached_connection(SAM_ADS_PRIVATES *privates) +{ + ADS_STRUCT *ads_struct; + ADS_STATUS ads_status; + + if (!privates->ads_struct) { + privates->ads_struct = ads_init_simple(); + ads_struct = privates->ads_struct; + ads_struct->server.ldap_uri = smb_xstrdup(privates->ldap_uri); + if ((!privates->ads_bind_dn) || (!*privates->ads_bind_dn)) { + ads_struct->auth.flags |= ADS_AUTH_ANON_BIND; + } else { + ads_struct->auth.user_name + = smb_xstrdup(privates->ads_bind_dn); + if (privates->ads_bind_pw) { + ads_struct->auth.password + = smb_xstrdup(privates->ads_bind_pw); + } + } + if (privates->bind_plaintext) { + ads_struct->auth.flags |= ADS_AUTH_SIMPLE_BIND; + } + } else { + ads_struct = privates->ads_struct; + } + + if (ads_struct->ld != NULL) { + /* connection has been opened. ping server. */ + struct sockaddr_un addr; + socklen_t len; + int sd; + if (ldap_get_option(ads_struct->ld, LDAP_OPT_DESC, &sd) == 0 && + getpeername(sd, (struct sockaddr *) &addr, &len) < 0) { + /* the other end has died. reopen. */ + ldap_unbind_ext(ads_struct->ld, NULL, NULL); + ads_struct->ld = NULL; + } + } + + if (ads_struct->ld != NULL) { + DEBUG(5,("sam_ads_cached_connection: allready connected to the LDAP server\n")); + return ADS_SUCCESS; + } + + ads_status = ads_connect(ads_struct); + + ads_status = ads_server_info(ads_struct); + if (!ADS_ERR_OK(ads_status)) { + DEBUG(0,("Can't set server info: %s\n",ads_errstr(ads_status))); + /* return ads_status; */ FIXME("for now we only warn!\n"); + } + + DEBUG(2, ("sam_ads_cached_connection: succesful connection to the LDAP server\n")); + return ADS_SUCCESS; +} + +static ADS_STATUS sam_ads_do_search(SAM_ADS_PRIVATES *privates, const char *bind_path, int scope, const char *exp, const char **attrs, void **res) +{ + ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL); + + ads_status = sam_ads_cached_connection(privates); + if (!ADS_ERR_OK(ads_status)) + return ads_status; + + return ads_do_search_retry(privates->ads_struct, bind_path, scope, exp, attrs, res); +} + + +/********************************************* +here we have to check the update serial number + - this is the core of the ldap cache +*********************************************/ +static ADS_STATUS sam_ads_usn_is_valid(SAM_ADS_PRIVATES *privates, uint32 usn_in, uint32 *usn_out) +{ + ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL); + + SAM_ASSERT(privates && privates->ads_struct && usn_out); + + ads_status = ads_USN(privates->ads_struct, usn_out); + if (!ADS_ERR_OK(ads_status)) + return ads_status; + + if (*usn_out == usn_in) + return ADS_SUCCESS; + + return ads_status; +} + +/*********************************************** +Initialize SAM_ACCOUNT_HANDLE from an ADS query +************************************************/ +/* not ready :-( */ +static ADS_STATUS ads_entry2sam_account_handle(SAM_ADS_PRIVATES *privates, SAM_ACCOUNT_HANDLE *account ,void *msg) +{ + ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_NO_SUCH_USER); + NTSTATUS nt_status = NT_STATUS_NO_SUCH_USER; + ADS_STRUCT *ads_struct = privates->ads_struct; + TALLOC_CTX *mem_ctx = account->mem_ctx; + char *tmp_str = NULL; + + SAM_ASSERT(privates && ads_struct && account && mem_ctx && msg); + + FIXME("should we really use ads_pull_username()(or ads_pull_string())?\n"); + if ((account->private.account_name = ads_pull_username(ads_struct, mem_ctx, msg))==NULL) { + DEBUG(0,("ads_pull_username failed\n")); + return ADS_ERROR_NT(NT_STATUS_NO_SUCH_USER); + } + + if ((account->private.full_name = ads_pull_string(ads_struct, mem_ctx, msg,"name"))==NULL) { + DEBUG(3,("ads_pull_string for 'name' failed - skip\n")); + } + + if ((account->private.acct_desc = ads_pull_string(ads_struct, mem_ctx, msg,"description"))!=NULL) { + DEBUG(3,("ads_pull_string for 'acct_desc' failed - skip\n")); + } + + if ((account->private.home_dir = ads_pull_string(ads_struct, mem_ctx, msg,"homeDirectory"))!=NULL) { + DEBUG(3,("ads_pull_string for 'homeDirectory' failed - skip\n")); + } + + if ((account->private.dir_drive = ads_pull_string(ads_struct, mem_ctx, msg,"homeDrive"))!=NULL) { + DEBUG(3,("ads_pull_string for 'homeDrive' failed - skip\n")); + } + + if ((account->private.profile_path = ads_pull_string(ads_struct, mem_ctx, msg,"profilePath"))!=NULL) { + DEBUG(3,("ads_pull_string for 'profilePath' failed - skip\n")); + } + + if ((account->private.logon_script = ads_pull_string(ads_struct, mem_ctx, msg,"scriptPath"))!=NULL) { + DEBUG(3,("ads_pull_string for 'scriptPath' failed - skip\n")); + } + + FIXME("check 'nsNPAllowDialIn' for munged_dial!\n"); + if ((account->private.munged_dial = ads_pull_string(ads_struct, mem_ctx, msg,"userParameters"))!=NULL) { + DEBUG(3,("ads_pull_string for 'userParameters' failed - skip\n")); + } + + if ((account->private.unix_home_dir = ads_pull_string(ads_struct, mem_ctx, msg,"msSFUHomeDrirectory"))!=NULL) { + DEBUG(3,("ads_pull_string for 'msSFUHomeDrirectory' failed - skip\n")); + } + +#if 0 + FIXME("use function intern mem_ctx for pwdLastSet\n"); + if ((tmp_str = ads_pull_string(ads_struct, mem_ctx, msg,"pwdLastSet"))!=NULL) { + DEBUG(3,("ads_pull_string for 'pwdLastSet' failed - skip\n")); + } else { + account->private.pass_last_set_time = ads_parse_nttime(tmp_str); + tmp_str = NULL; + + } +#endif + +#if 0 +typedef struct sam_account_handle { + TALLOC_CTX *mem_ctx; + uint32 access_granted; + const struct sam_methods *current_sam_methods; /* sam_methods creating this handle */ + void (*free_fn)(struct sam_account_handle **); + struct sam_account_data { + uint32 init_flag; + 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 * account_name; /* account_name string */ + SAM_DOMAIN_HANDLE * domain; /* domain of account */ + char *full_name; /* account's full name string */ + char *unix_home_dir; /* UNIX home directory string */ + char *home_dir; /* home directory string */ + char *dir_drive; /* home directory drive string */ + char *logon_script; /* logon script string */ + char *profile_path; /* profile path string */ + char *acct_desc; /* account description string */ + char *workstations; /* login from workstations string */ + char *unknown_str; /* don't know what this is, yet. */ + char *munged_dial; /* munged path name and dial-back tel number */ + DOM_SID account_sid; /* Primary Account 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; /* if Null not available */ + uint16 acct_ctrl; /* account info (ACB_xxxx bit-mask) */ + uint32 unknown_1; /* 0x00ff ffff */ + uint16 logon_divs; /* 168 - number of hours in a week */ + uint32 hours_len; /* normally 21 bytes */ + uint8 hours[MAX_HOURS_LEN]; + uint32 unknown_2; /* 0x0002 0000 */ + uint32 unknown_3; /* 0x0000 04ec */ + } private; +} SAM_ACCOUNT_HANDLE; +#endif + + return ads_status; +} + + +/*********************************************** +Initialize SAM_GROUP_ENUM from an ads entry +************************************************/ +/* not ready :-( */ +static ADS_STATUS ads_entry2sam_group_enum(SAM_ADS_PRIVATES *privates, TALLOC_CTX *mem_ctx, SAM_GROUP_ENUM **group_enum,const void *entry) +{ + ADS_STATUS ads_status = ADS_STATUS_UNSUCCESSFUL; + ADS_STRUCT *ads_struct = privates->ads_struct; + SAM_GROUP_ENUM __group_enum; + SAM_GROUP_ENUM *_group_enum = &__group_enum; + + SAM_ASSERT(privates && ads_struct && mem_ctx && group_enum && entry); + + *group_enum = _group_enum; + + DEBUG(3,("sam_ads: ads_entry2sam_account_handle\n")); + + if (!ads_pull_sid(ads_struct, &entry, "objectSid", &(_group_enum->sid))) { + DEBUG(0,("No sid for!?\n")); + return ADS_STATUS_UNSUCCESSFUL; + } + + if (!(_group_enum->group_name = ads_pull_string(ads_struct, mem_ctx, &entry, "sAMAccountName"))) { + DEBUG(0,("No groupname found")); + return ADS_STATUS_UNSUCCESSFUL; + } + + if (!(_group_enum->group_desc = ads_pull_string(ads_struct, mem_ctx, &entry, "desciption"))) { + DEBUG(0,("No description found")); + return ADS_STATUS_UNSUCCESSFUL; + } + + DEBUG(0,("sAMAccountName: %s\ndescription: %s\nobjectSid: %s\n", + _group_enum->group_name, + _group_enum->group_desc, + sid_string_static(&(_group_enum->sid)) + )); + + return ads_status; +} + +static ADS_STATUS sam_ads_access_check(SAM_ADS_PRIVATES *privates, const SEC_DESC *sd, const NT_USER_TOKEN *access_token, uint32 access_desired, uint32 *acc_granted) +{ + ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_ACCESS_DENIED); + NTSTATUS nt_status; + uint32 my_acc_granted; + + SAM_ASSERT(privates && sd && access_token); + /* acc_granted can be set to NULL */ + + /* the steps you need are: + 1. get_sec_desc for sid + 2. se_map_generic(accessdesired, generic_mapping) + 3. se_access_check() */ + + if (!se_access_check(sd, access_token, access_desired, (acc_granted)?acc_granted:&my_acc_granted, &nt_status)) { + DEBUG(3,("sam_ads_access_check: ACCESS DENIED\n")); + ads_status = ADS_ERROR_NT(nt_status); + return ads_status; + } + ads_status = ADS_ERROR_NT(nt_status); + return ads_status; +} + +static ADS_STATUS sam_ads_get_tree_sec_desc(SAM_ADS_PRIVATES *privates, const char *subtree, SEC_DESC **sd) +{ + ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER); + ADS_STRUCT *ads_struct = privates->ads_struct; + TALLOC_CTX *mem_ctx = privates->mem_ctx; + char *search_path; + void *sec_desc_res; + void *sec_desc_msg; + const char *sec_desc_attrs[] = {"nTSecurityDescriptor",NULL}; + + SAM_ASSERT(privates && ads_struct && mem_ctx && sd); + *sd = NULL; + + if (subtree) { + asprintf(&search_path, "%s%s",subtree,ads_struct->config.bind_path); + } else { + asprintf(&search_path, "%s",""); + } + ads_status = sam_ads_do_search(privates, search_path, LDAP_SCOPE_BASE, "(objectClass=*)", sec_desc_attrs, &sec_desc_res); + SAFE_FREE(search_path); + if (!ADS_ERR_OK(ads_status)) + return ads_status; + + if ((sec_desc_msg = ads_first_entry(ads_struct, sec_desc_res))==NULL) { + ads_status = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER); + return ads_status; + } + + if (!ads_pull_sd(ads_struct, mem_ctx, sec_desc_msg, sec_desc_attrs[0], sd)) { + *sd = NULL; + ads_status = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER); + return ads_status; + } + + return ads_status; +} + +static ADS_STATUS sam_ads_account_policy_get(SAM_ADS_PRIVATES *privates, int field, uint32 *value) +{ + ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER); + ADS_STRUCT *ads_struct = privates->ads_struct; + void *ap_res; + void *ap_msg; + const char *ap_attrs[] = {"minPwdLength",/* AP_MIN_PASSWORD_LEN */ + "pwdHistoryLength",/* AP_PASSWORD_HISTORY */ + "AP_USER_MUST_LOGON_TO_CHG_PASS",/* AP_USER_MUST_LOGON_TO_CHG_PASS */ + "maxPwdAge",/* AP_MAX_PASSWORD_AGE */ + "minPwdAge",/* AP_MIN_PASSWORD_AGE */ + "lockoutDuration",/* AP_LOCK_ACCOUNT_DURATION */ + "AP_RESET_COUNT_TIME",/* AP_RESET_COUNT_TIME */ + "AP_BAD_ATTEMPT_LOCKOUT",/* AP_BAD_ATTEMPT_LOCKOUT */ + "AP_TIME_TO_LOGOUT",/* AP_TIME_TO_LOGOUT */ + NULL}; + /*lockOutObservationWindow + lockoutThreshold $ pwdProperties*/ + static uint32 ap[9]; + static uint32 ap_usn = 0; + uint32 tmp_usn = 0; + + SAM_ASSERT(privates && ads_struct && value); + + FIXME("We need to decode all account_policy attributes!\n"); + + ads_status = sam_ads_usn_is_valid(privates,ap_usn,&tmp_usn); + if (!ADS_ERR_OK(ads_status)) { + ads_status = sam_ads_do_search(privates, ads_struct->config.bind_path, LDAP_SCOPE_BASE, "(objectClass=*)", ap_attrs, &ap_res); + if (!ADS_ERR_OK(ads_status)) + return ads_status; + + if (ads_count_replies(ads_struct, ap_res) != 1) { + ads_msgfree(ads_struct, ap_res); + return ADS_ERROR(LDAP_NO_RESULTS_RETURNED); + } + + if (!(ap_msg = ads_first_entry(ads_struct, ap_res))) { + ads_msgfree(ads_struct, ap_res); + return ADS_ERROR(LDAP_NO_RESULTS_RETURNED); + } + + if (!ads_pull_uint32(ads_struct, ap_msg, ap_attrs[0], &ap[0])) { + /* AP_MIN_PASSWORD_LEN */ + ap[0] = MINPASSWDLENGTH;/* 5 chars minimum */ + } + if (!ads_pull_uint32(ads_struct, ap_msg, ap_attrs[1], &ap[1])) { + /* AP_PASSWORD_HISTORY */ + ap[1] = 0;/* don't keep any old password */ + } + if (!ads_pull_uint32(ads_struct, ap_msg, ap_attrs[2], &ap[2])) { + /* AP_USER_MUST_LOGON_TO_CHG_PASS */ + ap[2] = 0;/* don't force user to logon */ + } + if (!ads_pull_uint32(ads_struct, ap_msg, ap_attrs[3], &ap[3])) { + /* AP_MAX_PASSWORD_AGE */ + ap[3] = MAX_PASSWORD_AGE;/* 21 days */ + } + if (!ads_pull_uint32(ads_struct, ap_msg, ap_attrs[4], &ap[4])) { + /* AP_MIN_PASSWORD_AGE */ + ap[4] = 0;/* 0 days */ + } + if (!ads_pull_uint32(ads_struct, ap_msg, ap_attrs[5], &ap[5])) { + /* AP_LOCK_ACCOUNT_DURATION */ + ap[5] = 0;/* lockout for 0 minutes */ + } + if (!ads_pull_uint32(ads_struct, ap_msg, ap_attrs[6], &ap[6])) { + /* AP_RESET_COUNT_TIME */ + ap[6] = 0;/* reset immediatly */ + } + if (!ads_pull_uint32(ads_struct, ap_msg, ap_attrs[7], &ap[7])) { + /* AP_BAD_ATTEMPT_LOCKOUT */ + ap[7] = 0;/* don't lockout */ + } + if (!ads_pull_uint32(ads_struct, ap_msg, ap_attrs[8], &ap[8])) { + /* AP_TIME_TO_LOGOUT */ + ap[8] = -1;/* don't force logout */ + } + + ads_msgfree(ads_struct, ap_res); + ap_usn = tmp_usn; + } + + switch(field) { + case AP_MIN_PASSWORD_LEN: + *value = ap[0]; + ads_status = ADS_ERROR_NT(NT_STATUS_OK); + break; + case AP_PASSWORD_HISTORY: + *value = ap[1]; + ads_status = ADS_ERROR_NT(NT_STATUS_OK); + break; + case AP_USER_MUST_LOGON_TO_CHG_PASS: + *value = ap[2]; + ads_status = ADS_ERROR_NT(NT_STATUS_OK); + break; + case AP_MAX_PASSWORD_AGE: + *value = ap[3]; + ads_status = ADS_ERROR_NT(NT_STATUS_OK); + break; + case AP_MIN_PASSWORD_AGE: + *value = ap[4]; + ads_status = ADS_ERROR_NT(NT_STATUS_OK); + break; + case AP_LOCK_ACCOUNT_DURATION: + *value = ap[5]; + ads_status = ADS_ERROR_NT(NT_STATUS_OK); + break; + case AP_RESET_COUNT_TIME: + *value = ap[6]; + ads_status = ADS_ERROR_NT(NT_STATUS_OK); + break; + case AP_BAD_ATTEMPT_LOCKOUT: + *value = ap[7]; + ads_status = ADS_ERROR_NT(NT_STATUS_OK); + break; + case AP_TIME_TO_LOGOUT: + *value = ap[8]; + ads_status = ADS_ERROR_NT(NT_STATUS_OK); + break; + default: *value = 0; break; + } + + return ads_status; +} + + +/********************************** +Now the functions off the SAM API +***********************************/ + +/* General API */ +static NTSTATUS sam_ads_get_sec_desc(const SAM_METHODS *sam_method, const NT_USER_TOKEN *access_token, + const DOM_SID *sid, SEC_DESC **sd) +{ + ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL); + SAM_ADS_PRIVATES *privates = (struct sam_ads_privates *)sam_method->private_data; + ADS_STRUCT *ads_struct = privates->ads_struct; + TALLOC_CTX *mem_ctx; + char *sidstr,*filter; + void *sec_desc_res; + void *sec_desc_msg; + const char *sec_desc_attrs[] = {"nTSecurityDescriptor",NULL}; + fstring sid_str; + SEC_DESC *my_sd; + + SAM_ASSERT(sam_method && access_token && sid && sd); + + ads_status = sam_ads_get_tree_sec_desc(privates, ADS_ROOT_TREE, &my_sd); + if (!ADS_ERR_OK(ads_status)) + return ads_ntstatus(ads_status); + + ads_status = sam_ads_access_check(privates, my_sd, access_token, GENERIC_RIGHTS_DOMAIN_READ, NULL); + + if (!ADS_ERR_OK(ads_status)) + return ads_ntstatus(ads_status); + + sidstr = sid_binstring(sid); + if (asprintf(&filter, "(objectSid=%s)", sidstr) == -1) { + SAFE_FREE(sidstr); + return NT_STATUS_NO_MEMORY; + } + + SAFE_FREE(sidstr); + + ads_status = sam_ads_do_search(privates,ads_struct->config.bind_path, + LDAP_SCOPE_SUBTREE, filter, sec_desc_attrs, + &sec_desc_res); + SAFE_FREE(filter); + + if (!ADS_ERR_OK(ads_status)) { + return ads_ntstatus(ads_status); + } + + if (!(mem_ctx = talloc_init("sec_desc parse in sam_ads"))) { + DEBUG(1, ("talloc_init() failed for sec_desc parse context in sam_ads")); + ads_msgfree(ads_struct, sec_desc_res); + return NT_STATUS_NO_MEMORY; + } + + if (ads_count_replies(ads_struct, sec_desc_res) != 1) { + DEBUG(1,("sam_ads_get_sec_desc: duplicate or 0 results for sid %s\n", + sid_to_string(sid_str, sid))); + talloc_destroy(mem_ctx); + ads_msgfree(ads_struct, sec_desc_res); + return NT_STATUS_UNSUCCESSFUL; + } + + if (!(sec_desc_msg = ads_first_entry(ads_struct, sec_desc_res))) { + talloc_destroy(mem_ctx); + ads_msgfree(ads_struct, sec_desc_res); + return NT_STATUS_INVALID_PARAMETER; + } + + if (!ads_pull_sd(ads_struct, mem_ctx, sec_desc_msg, sec_desc_attrs[0], sd)) { + ads_status = ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER); + talloc_destroy(mem_ctx); + ads_msgfree(ads_struct, sec_desc_res); + return ads_ntstatus(ads_status); + } + + /* now, were we allowed to see the SD we just got? */ + + ads_msgfree(ads_struct, sec_desc_res); + talloc_destroy(mem_ctx); + return ads_ntstatus(ads_status); +} + +static NTSTATUS sam_ads_set_sec_desc(const SAM_METHODS *sam_method, const NT_USER_TOKEN *access_token, + const DOM_SID *sid, const SEC_DESC *sd) +{ + ADS_STATUS ads_status = ADS_STATUS_NOT_IMPLEMENTED; + DEBUG(0,("sam_ads: %s was called!\n",FUNCTION_MACRO)); + SAM_ASSERT(sam_method); + return ads_ntstatus(ads_status); +} + + +static NTSTATUS sam_ads_lookup_sid(const SAM_METHODS *sam_method, const NT_USER_TOKEN *access_token, + TALLOC_CTX *mem_ctx, const DOM_SID *sid, char **name, + enum SID_NAME_USE *type) +{ + ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL); + SAM_ADS_PRIVATES *privates = (struct sam_ads_privates *)sam_method->private_data; + ADS_STRUCT *ads_struct = privates->ads_struct; + SEC_DESC *my_sd; + + SAM_ASSERT(sam_method && access_token && mem_ctx && sid && name && type); + + ads_status = sam_ads_get_tree_sec_desc(privates, ADS_ROOT_TREE, &my_sd); + if (!ADS_ERR_OK(ads_status)) + return ads_ntstatus(ads_status); + + ads_status = sam_ads_access_check(privates, my_sd, access_token, GENERIC_RIGHTS_DOMAIN_READ, NULL); + if (!ADS_ERR_OK(ads_status)) + return ads_ntstatus(ads_status); + + return ads_sid_to_name(ads_struct, mem_ctx, sid, name, type); +} + +static NTSTATUS sam_ads_lookup_name(const SAM_METHODS *sam_method, const NT_USER_TOKEN *access_token, + const char *name, DOM_SID *sid, enum SID_NAME_USE *type) +{ + ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL); + SAM_ADS_PRIVATES *privates = (struct sam_ads_privates *)sam_method->private_data; + ADS_STRUCT *ads_struct = privates->ads_struct; + SEC_DESC *my_sd; + + SAM_ASSERT(sam_method && access_token && name && sid && type); + + ads_status = sam_ads_get_tree_sec_desc(privates, ADS_ROOT_TREE, &my_sd); + if (!ADS_ERR_OK(ads_status)) + return ads_ntstatus(ads_status); + + ads_status = sam_ads_access_check(privates, my_sd, access_token, GENERIC_RIGHTS_DOMAIN_READ, NULL); + if (!ADS_ERR_OK(ads_status)) + return ads_ntstatus(ads_status); + + return ads_name_to_sid(ads_struct, name, sid, type); +} + + +/* Domain API */ + +static NTSTATUS sam_ads_update_domain(const SAM_METHODS *sam_method, const SAM_DOMAIN_HANDLE *domain) +{ + ADS_STATUS ads_status = ADS_STATUS_NOT_IMPLEMENTED; + DEBUG(0,("sam_ads: %s was called!\n",FUNCTION_MACRO)); + SAM_ASSERT(sam_method); + return ads_ntstatus(ads_status); +} + +static NTSTATUS sam_ads_get_domain_handle(const SAM_METHODS *sam_method, const NT_USER_TOKEN *access_token, + const uint32 access_desired, SAM_DOMAIN_HANDLE **domain) +{ + ADS_STATUS ads_status = ADS_STATUS_NOT_IMPLEMENTED; + SAM_ADS_PRIVATES *privates = (struct sam_ads_privates *)sam_method->private_data; + TALLOC_CTX *mem_ctx = privates->mem_ctx; /*Fix me is this right??? */ + SAM_DOMAIN_HANDLE *dom_handle = NULL; + SEC_DESC *sd; + uint32 acc_granted; + uint32 tmp_value; + + DEBUG(5,("sam_ads_get_domain_handle: %d\n",__LINE__)); + + SAM_ASSERT(sam_method && access_token && domain); + + (*domain) = NULL; + + if ((dom_handle = talloc(mem_ctx, sizeof(SAM_DOMAIN_HANDLE))) == NULL) { + DEBUG(0,("failed to talloc dom_handle\n")); + ads_status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY); + return ads_ntstatus(ads_status); + } + + ZERO_STRUCTP(dom_handle); + + dom_handle->mem_ctx = mem_ctx; /*Fix me is this right??? */ + dom_handle->free_fn = NULL; + dom_handle->current_sam_methods = sam_method; + + /* check if access can be granted as requested */ + + ads_status = sam_ads_get_tree_sec_desc(privates, ADS_ROOT_TREE, &sd); + if (!ADS_ERR_OK(ads_status)) + return ads_ntstatus(ads_status); + + ads_status = sam_ads_access_check(privates, sd, access_token, access_desired, &acc_granted); + if (!ADS_ERR_OK(ads_status)) + return ads_ntstatus(ads_status); + + dom_handle->access_granted = acc_granted; + + /* fill all the values of dom_handle */ + sid_copy(&dom_handle->private.sid, &sam_method->domain_sid); + dom_handle->private.name = smb_xstrdup(sam_method->domain_name); + dom_handle->private.servername = "WHOKNOWS"; /* what is the servername */ + + /*Fix me: sam_ads_account_policy_get() return ADS_STATUS! */ + ads_status = sam_ads_account_policy_get(privates, AP_MAX_PASSWORD_AGE, &tmp_value); + if (!ADS_ERR_OK(ads_status)) { + DEBUG(4,("sam_ads_account_policy_get failed for max password age. Useing default\n")); + tmp_value = MAX_PASSWORD_AGE; + } + unix_to_nt_time_abs(&dom_handle->private.max_passwordage,tmp_value); + + ads_status = sam_ads_account_policy_get(privates, AP_MIN_PASSWORD_AGE, &tmp_value); + if (!ADS_ERR_OK(ads_status)) { + DEBUG(4,("sam_ads_account_policy_get failed for min password age. Useing default\n")); + tmp_value = 0; + } + unix_to_nt_time_abs(&dom_handle->private.min_passwordage, tmp_value); + + ads_status = sam_ads_account_policy_get(privates, AP_LOCK_ACCOUNT_DURATION, &tmp_value); + if (!ADS_ERR_OK(ads_status)) { + DEBUG(4,("sam_ads_account_policy_get failed for lockout duration. Useing default\n")); + tmp_value = 0; + } + unix_to_nt_time_abs(&dom_handle->private.lockout_duration, tmp_value); + + ads_status = sam_ads_account_policy_get(privates, AP_RESET_COUNT_TIME, &tmp_value); + if (!ADS_ERR_OK(ads_status)) { + DEBUG(4,("sam_ads_account_policy_get failed for time till locout count is reset. Useing default\n")); + tmp_value = 0; + } + unix_to_nt_time_abs(&dom_handle->private.reset_count, tmp_value); + + ads_status = sam_ads_account_policy_get(privates, AP_MIN_PASSWORD_LEN, &tmp_value); + if (!ADS_ERR_OK(ads_status)) { + DEBUG(4,("sam_ads_account_policy_get failed for min password length. Useing default\n")); + tmp_value = 0; + } + dom_handle->private.min_passwordlength = (uint16)tmp_value; + + ads_status = sam_ads_account_policy_get(privates, AP_PASSWORD_HISTORY, &tmp_value); + if (!ADS_ERR_OK(ads_status)) { + DEBUG(4,("sam_ads_account_policy_get failed password history. Useing default\n")); + tmp_value = 0; + } + dom_handle->private.password_history = (uint16)tmp_value; + + ads_status = sam_ads_account_policy_get(privates, AP_BAD_ATTEMPT_LOCKOUT, &tmp_value); + if (!ADS_ERR_OK(ads_status)) { + DEBUG(4,("sam_ads_account_policy_get failed for bad attempts till lockout. Useing default\n")); + tmp_value = 0; + } + dom_handle->private.lockout_count = (uint16)tmp_value; + + ads_status = sam_ads_account_policy_get(privates, AP_TIME_TO_LOGOUT, &tmp_value); + if (!ADS_ERR_OK(ads_status)) { + DEBUG(4,("sam_ads_account_policy_get failed for force logout. Useing default\n")); + tmp_value = -1; + } + + ads_status = sam_ads_account_policy_get(privates, AP_USER_MUST_LOGON_TO_CHG_PASS, &tmp_value); + if (!ADS_ERR_OK(ads_status)) { + DEBUG(4,("sam_ads_account_policy_get failed for user must login to change password. Useing default\n")); + tmp_value = 0; + } + + /* should the real values of num_accounts, num_groups and num_aliases be retreved? + * I think it is to expensive to bother + */ + dom_handle->private.num_accounts = 3; + dom_handle->private.num_groups = 4; + dom_handle->private.num_aliases = 5; + + *domain = dom_handle; + + ads_status = ADS_ERROR_NT(NT_STATUS_OK); + return ads_ntstatus(ads_status); +} + +/* Account API */ +static NTSTATUS sam_ads_create_account(const SAM_METHODS *sam_method, + const NT_USER_TOKEN *access_token, uint32 access_desired, + const char *account_name, uint16 acct_ctrl, SAM_ACCOUNT_HANDLE **account) +{ + ADS_STATUS ads_status = ADS_STATUS_NOT_IMPLEMENTED; + SAM_ADS_PRIVATES *privates = (struct sam_ads_privates *)sam_method->private_data; + SEC_DESC *sd = NULL; + uint32 acc_granted; + + SAM_ASSERT(sam_method && privates && access_token && account_name && account); + + ads_status = sam_ads_get_tree_sec_desc(privates, ADS_SUBTREE_USERS, &sd); + if (!ADS_ERR_OK(ads_status)) + return ads_ntstatus(ads_status); + + ads_status = sam_ads_access_check(privates, sd, access_token, access_desired, &acc_granted); + if (!ADS_ERR_OK(ads_status)) + return ads_ntstatus(ads_status); + + ads_status = ADS_ERROR_NT(sam_init_account(account)); + if (!ADS_ERR_OK(ads_status)) + return ads_ntstatus(ads_status); + + (*account)->access_granted = acc_granted; + + return ads_ntstatus(ads_status); +} + +static NTSTATUS sam_ads_add_account(const SAM_METHODS *sam_method, const SAM_ACCOUNT_HANDLE *account) +{ + ADS_STATUS ads_status = ADS_ERROR(LDAP_NO_MEMORY); + SAM_ADS_PRIVATES *privates = (struct sam_ads_privates *)sam_method->private_data; + ADS_STRUCT *ads_struct = privates->ads_struct; + TALLOC_CTX *mem_ctx = privates->mem_ctx; + ADS_MODLIST mods; + uint16 acct_ctrl; + char *new_dn; + SEC_DESC *sd; + uint32 acc_granted; + + SAM_ASSERT(sam_method && account); + + ads_status = ADS_ERROR_NT(sam_get_account_acct_ctrl(account,&acct_ctrl)); + if (!ADS_ERR_OK(ads_status)) + goto done; + + if ((acct_ctrl & ACB_WSTRUST)||(acct_ctrl & ACB_SVRTRUST)) { + /* Computer account */ + char *name,*controlstr; + char *hostname,*host_upn,*host_spn; + const char *objectClass[] = {"top", "person", "organizationalPerson", + "user", "computer", NULL}; + + ads_status = ADS_ERROR_NT(sam_get_account_name(account,&name)); + if (!ADS_ERR_OK(ads_status)) + goto done; + + if (!(host_upn = talloc_asprintf(mem_ctx, "%s@%s", name, ads_struct->config.realm))) { + ads_status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY); + goto done; + } + + if (!(new_dn = talloc_asprintf(mem_ctx, "CN=%s,CN=Computers,%s", hostname, + ads_struct->config.bind_path))) { + ads_status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY); + goto done; + } + + if (!(controlstr = talloc_asprintf(mem_ctx, "%u", ads_acb2uf(acct_ctrl)))) { + ads_status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY); + goto done; + } + + if (!(mods = ads_init_mods(mem_ctx))) { + ads_status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY); + goto done; + } + + ads_status = ads_mod_str(mem_ctx, &mods, "cn", hostname); + if (!ADS_ERR_OK(ads_status)) + goto done; + ads_status = ads_mod_strlist(mem_ctx, &mods, "objectClass", objectClass); + if (!ADS_ERR_OK(ads_status)) + goto done; + ads_status = ads_mod_str(mem_ctx, &mods, "userPrincipalName", host_upn); + if (!ADS_ERR_OK(ads_status)) + goto done; + ads_status = ads_mod_str(mem_ctx, &mods, "displayName", hostname); + if (!ADS_ERR_OK(ads_status)) + goto done; + ads_status = ads_mod_str(mem_ctx, &mods, "sAMAccountName", name); + if (!ADS_ERR_OK(ads_status)) + goto done; + ads_status = ads_mod_str(mem_ctx, &mods, "userAccountControl", controlstr); + if (!ADS_ERR_OK(ads_status)) + goto done; + + ads_status = ads_mod_str(mem_ctx, &mods, "servicePrincipalName", host_spn); + if (!ADS_ERR_OK(ads_status)) + goto done; + ads_status = ads_mod_str(mem_ctx, &mods, "dNSHostName", hostname); + if (!ADS_ERR_OK(ads_status)) + goto done; + ads_status = ads_mod_str(mem_ctx, &mods, "userAccountControl", controlstr); + if (!ADS_ERR_OK(ads_status)) + goto done; + /* ads_status = ads_mod_str(mem_ctx, &mods, "operatingSystem", "Samba"); + if (!ADS_ERR_OK(ads_status)) + goto done; + *//* ads_status = ads_mod_str(mem_ctx, &mods, "operatingSystemVersion", VERSION); + if (!ADS_ERR_OK(ads_status)) + goto done; + */ + /* End Computer account */ + } else { + /* User account*/ + char *upn, *controlstr; + char *name, *fullname; + const char *objectClass[] = {"top", "person", "organizationalPerson", + "user", NULL}; + + ads_status = ADS_ERROR_NT(sam_get_account_name(account,&name)); + if (!ADS_ERR_OK(ads_status)) + goto done; + + ads_status = ADS_ERROR_NT(sam_get_account_fullname(account,&fullname)); + if (!ADS_ERR_OK(ads_status)) + goto done; + + if (!(upn = talloc_asprintf(mem_ctx, "%s@%s", name, ads_struct->config.realm))) { + ads_status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY); + goto done; + } + + if (!(new_dn = talloc_asprintf(mem_ctx, "CN=%s,CN=Users,%s", fullname, + ads_struct->config.bind_path))) { + ads_status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY); + goto done; + } + + if (!(controlstr = talloc_asprintf(mem_ctx, "%u", ads_acb2uf(acct_ctrl)))) { + ads_status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY); + goto done; + } + + if (!(mods = ads_init_mods(mem_ctx))) { + ads_status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY); + goto done; + } + + ads_status = ads_mod_str(mem_ctx, &mods, "cn", fullname); + if (!ADS_ERR_OK(ads_status)) + goto done; + ads_status = ads_mod_strlist(mem_ctx, &mods, "objectClass", objectClass); + if (!ADS_ERR_OK(ads_status)) + goto done; + ads_status = ads_mod_str(mem_ctx, &mods, "userPrincipalName", upn); + if (!ADS_ERR_OK(ads_status)) + goto done; + ads_status = ads_mod_str(mem_ctx, &mods, "displayName", fullname); + if (!ADS_ERR_OK(ads_status)) + goto done; + ads_status = ads_mod_str(mem_ctx, &mods, "sAMAccountName", name); + if (!ADS_ERR_OK(ads_status)) + goto done; + ads_status = ads_mod_str(mem_ctx, &mods, "userAccountControl", controlstr); + if (!ADS_ERR_OK(ads_status)) + goto done; + }/* End User account */ + + /* Finally at the account */ + ads_status = ads_gen_add(ads_struct, new_dn, mods); + +done: + return ads_ntstatus(ads_status); +} + +static NTSTATUS sam_ads_update_account(const SAM_METHODS *sam_method, const SAM_ACCOUNT_HANDLE *account) +{ + ADS_STATUS ads_status = ADS_STATUS_NOT_IMPLEMENTED; + DEBUG(0,("sam_ads: %s was called!\n",FUNCTION_MACRO)); + SAM_ASSERT(sam_method); + return ads_ntstatus(ads_status); +} + +static NTSTATUS sam_ads_delete_account(const SAM_METHODS *sam_method, const SAM_ACCOUNT_HANDLE *account) +{ + ADS_STATUS ads_status = ADS_STATUS_NOT_IMPLEMENTED; + DEBUG(0,("sam_ads: %s was called!\n",FUNCTION_MACRO)); + SAM_ASSERT(sam_method); + + + + return ads_ntstatus(ads_status); +} + +static NTSTATUS sam_ads_enum_accounts(const SAM_METHODS *sam_method, const NT_USER_TOKEN *access_token, uint16 acct_ctrl, uint32 *account_count, SAM_ACCOUNT_ENUM **accounts) +{ + ADS_STATUS ads_status = ADS_STATUS_NOT_IMPLEMENTED; + DEBUG(0,("sam_ads: %s was called!\n",FUNCTION_MACRO)); + SAM_ASSERT(sam_method); + return ads_ntstatus(ads_status); +} + +#if 0 +static NTSTATUS sam_ads_get_account_by_sid(const SAM_METHODS *sam_method, const NT_USER_TOKEN *access_token, const uint32 access_desired, const DOM_SID *account_sid, SAM_ACCOUNT_HANDLE **account) +{ + ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL); + SAM_ADS_PRIVATES *privates = (struct sam_ads_privates *)sam_method->private_data; + ADS_STRUCT *ads_struct = privates->ads_struct; + TALLOC_CTX *mem_ctx = privates->mem_ctx; + SEC_DESC *sd = NULL; + uint32 acc_granted; + + SAM_ASSERT(sam_method && privates && ads_struct && access_token && account_sid && account); + + ads_status = ADS_ERROR_NT(sam_ads_get_sec_desc(sam_method, access_token, account_sid, &my_sd)); + if (!ADS_ERR_OK(ads_status)) + return ads_ntstatus(ads_status); + + ads_status = sam_ads_access_check(privates, sd, access_token, access_desired, &acc_granted); + if (!ADS_ERR_OK(ads_status)) + return ads_ntstatus(ads_status); + + ads_status = ADS_ERROR_NT(sam_init_account(account)); + if (!ADS_ERR_OK(ads_status)) + return ads_ntstatus(ads_status); + + (*account)->access_granted = acc_granted; + + return ads_ntstatus(ads_status); +} +#else +static NTSTATUS sam_ads_get_account_by_sid(const SAM_METHODS *sam_method, const NT_USER_TOKEN *access_token, const uint32 access_desired, const DOM_SID *account_sid, SAM_ACCOUNT_HANDLE **account) +{ + ADS_STATUS ads_status = ADS_STATUS_NOT_IMPLEMENTED; + DEBUG(0,("sam_ads: %s was called!\n",FUNCTION_MACRO)); + SAM_ASSERT(sam_method); + return ads_ntstatus(ads_status); +} +#endif + +#if 0 +static NTSTATUS sam_ads_get_account_by_name(const SAM_METHODS *sam_method, const NT_USER_TOKEN *access_token, const uint32 access_desired, const char *account_name, SAM_ACCOUNT_HANDLE **account) +{ + ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL); + SAM_ADS_PRIVATES *privates = (struct sam_ads_privates *)sam_method->private_data; + ADS_STRUCT *ads_struct = privates->ads_struct; + TALLOC_CTX *mem_ctx = privates->mem_ctx; + SEC_DESC *sd = NULL; + uint32 acc_granted; + + SAM_ASSERT(sam_method && privates && ads_struct && access_token && account_name && account); + + ads_status = sam_ads_get_tree_sec_desc(privates, ADS_ROOT_TREE, &sd); + if (!ADS_ERR_OK(ads_status)) + return ads_ntstatus(ads_status); + + ads_status = sam_ads_access_check(privates, sd, access_token, access_desired, &acc_granted); + if (!ADS_ERR_OK(ads_status)) + return ads_ntstatus(ads_status); + + ads_status = ADS_ERROR_NT(sam_init_account(account)); + if (!ADS_ERR_OK(ads_status)) + return ads_ntstatus(ads_status); + + (*account)->access_granted = acc_granted; + + return ads_ntstatus(ads_status); +} +#else +static NTSTATUS sam_ads_get_account_by_name(const SAM_METHODS *sam_method, const NT_USER_TOKEN *access_token, const uint32 access_desired, const char *account_name, SAM_ACCOUNT_HANDLE **account) +{ + ADS_STATUS ads_status = ADS_STATUS_NOT_IMPLEMENTED; + DEBUG(0,("sam_ads: %s was called!\n",FUNCTION_MACRO)); + SAM_ASSERT(sam_method); + return ads_ntstatus(ads_status); +} +#endif + +/* Group API */ +static NTSTATUS sam_ads_create_group(const SAM_METHODS *sam_method, const NT_USER_TOKEN *access_token, uint32 access_desired, const char *group_name, uint16 group_ctrl, SAM_GROUP_HANDLE **group) +{ + ADS_STATUS ads_status = ADS_STATUS_NOT_IMPLEMENTED; + DEBUG(0,("sam_ads: %s was called!\n",FUNCTION_MACRO)); + SAM_ASSERT(sam_method); + return ads_ntstatus(ads_status); +} + +static NTSTATUS sam_ads_add_group(const SAM_METHODS *sam_method, const SAM_GROUP_HANDLE *group) +{ + ADS_STATUS ads_status = ADS_STATUS_NOT_IMPLEMENTED; + DEBUG(0,("sam_ads: %s was called!\n",FUNCTION_MACRO)); + SAM_ASSERT(sam_method); + return ads_ntstatus(ads_status); +} + +static NTSTATUS sam_ads_update_group(const SAM_METHODS *sam_method, const SAM_GROUP_HANDLE *group) +{ + ADS_STATUS ads_status = ADS_STATUS_NOT_IMPLEMENTED; + DEBUG(0,("sam_ads: %s was called!\n",FUNCTION_MACRO)); + SAM_ASSERT(sam_method); + return ads_ntstatus(ads_status); +} + +static NTSTATUS sam_ads_delete_group(const SAM_METHODS *sam_method, const SAM_GROUP_HANDLE *group) +{ + ADS_STATUS ads_status = ADS_STATUS_NOT_IMPLEMENTED; + DEBUG(0,("sam_ads: %s was called!\n",FUNCTION_MACRO)); + SAM_ASSERT(sam_method); + return ads_ntstatus(ads_status); +} + +static NTSTATUS sam_ads_enum_groups(const SAM_METHODS *sam_method, const NT_USER_TOKEN *access_token, const uint16 group_ctrl, uint32 *groups_count, SAM_GROUP_ENUM **groups) +{ + ADS_STATUS ads_status = ADS_STATUS_NOT_IMPLEMENTED; + SAM_ADS_PRIVATES *privates = (struct sam_ads_privates *)sam_method->private_data; + ADS_STRUCT *ads_struct = privates->ads_struct; + TALLOC_CTX *mem_ctx = privates->mem_ctx; + void *res = NULL; + void *msg = NULL; + char *filter = NULL; + int i = 0; + + /* get only these LDAP attributes, witch we really need for a group */ + const char *group_enum_attrs[] = {"objectSid", + "description", + "sAMAcountName", + NULL}; + + SAM_ASSERT(sam_method && access_token && groups_count && groups); + + *groups_count = 0; + + DEBUG(3,("ads: enum_dom_groups\n")); + + FIXME("get only group from the wanted Type!\n"); + asprintf(&filter, "(&(objectClass=group)(groupType=%s))", "*"); + ads_status = sam_ads_do_search(privates, ads_struct->config.bind_path, LDAP_SCOPE_SUBTREE, filter, group_enum_attrs, &res); + if (!ADS_ERR_OK(ads_status)) { + DEBUG(1,("enum_groups ads_search: %s\n", ads_errstr(ads_status))); + } + + *groups_count = ads_count_replies(ads_struct, res); + if (*groups_count == 0) { + DEBUG(1,("enum_groups: No groups found\n")); + } + + (*groups) = talloc_zero(mem_ctx, (*groups_count) * sizeof(**groups)); + if (!*groups) { + ads_status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY); + } + + for (msg = ads_first_entry(ads_struct, res); msg; msg = ads_next_entry(ads_struct, msg)) { + uint32 grouptype; + + if (!ads_pull_uint32(ads_struct, msg, "groupType", &grouptype)) { + ; + } else { + (*groups)->group_ctrl = ads_gtype2gcb(grouptype); + } + + if (!((*groups)->group_name = ads_pull_string(ads_struct, mem_ctx, msg, "sAMAccountName"))) { + ; + } + + if (!((*groups)->group_desc = ads_pull_string(ads_struct, mem_ctx, msg, "description"))) { + ; + } + + if (!ads_pull_sid(ads_struct, msg, "objectSid", &((*groups)->sid))) { + DEBUG(1,("No sid for group %s !?\n", (*groups)->group_name)); + continue; + } + + i++; + } + + (*groups_count) = i; + + ads_status = ADS_ERROR_NT(NT_STATUS_OK); + + DEBUG(3,("ads enum_dom_groups gave %d entries\n", (*groups_count))); + + if (res) ads_msgfree(ads_struct, res); + + return ads_ntstatus(ads_status); +} + +static NTSTATUS sam_ads_get_group_by_sid(const SAM_METHODS *sam_method, const NT_USER_TOKEN *access_token, const uint32 access_desired, const DOM_SID *groupsid, SAM_GROUP_HANDLE **group) +{ + ADS_STATUS ads_status = ADS_STATUS_NOT_IMPLEMENTED; + DEBUG(0,("sam_ads: %s was called!\n",FUNCTION_MACRO)); + SAM_ASSERT(sam_method); + return ads_ntstatus(ads_status); +} + +static NTSTATUS sam_ads_get_group_by_name(const SAM_METHODS *sam_method, const NT_USER_TOKEN *access_token, const uint32 access_desired, const char *name, SAM_GROUP_HANDLE **group) +{ + ADS_STATUS ads_status = ADS_STATUS_NOT_IMPLEMENTED; + DEBUG(0,("sam_ads: %s was called!\n",FUNCTION_MACRO)); + SAM_ASSERT(sam_method); + return ads_ntstatus(ads_status); +} + +static NTSTATUS sam_ads_add_member_to_group(const SAM_METHODS *sam_method, const SAM_GROUP_HANDLE *group, const SAM_GROUP_MEMBER *member) +{ + ADS_STATUS ads_status = ADS_STATUS_NOT_IMPLEMENTED; + DEBUG(0,("sam_ads: %s was called!\n",FUNCTION_MACRO)); + SAM_ASSERT(sam_method); + return ads_ntstatus(ads_status); +} + +static NTSTATUS sam_ads_delete_member_from_group(const SAM_METHODS *sam_method, const SAM_GROUP_HANDLE *group, const SAM_GROUP_MEMBER *member) +{ + ADS_STATUS ads_status = ADS_STATUS_NOT_IMPLEMENTED; + DEBUG(0,("sam_ads: %s was called!\n",FUNCTION_MACRO)); + SAM_ASSERT(sam_method); + return ads_ntstatus(ads_status); +} + +static NTSTATUS sam_ads_enum_groupmembers(const SAM_METHODS *sam_method, const SAM_GROUP_HANDLE *group, uint32 *members_count, SAM_GROUP_MEMBER **members) +{ + ADS_STATUS ads_status = ADS_STATUS_NOT_IMPLEMENTED; + DEBUG(0,("sam_ads: %s was called!\n",FUNCTION_MACRO)); + SAM_ASSERT(sam_method); + return ads_ntstatus(ads_status); +} + +static NTSTATUS sam_ads_get_groups_of_sid(const SAM_METHODS *sam_method, const NT_USER_TOKEN *access_token, const DOM_SID **sids, const uint16 group_ctrl, uint32 *group_count, SAM_GROUP_ENUM **groups) +{ + ADS_STATUS ads_status = ADS_STATUS_NOT_IMPLEMENTED; + DEBUG(0,("sam_ads: %s was called!\n",FUNCTION_MACRO)); + SAM_ASSERT(sam_method); + return ads_ntstatus(ads_status); +} + +/********************************** +Free our private data +***********************************/ +static void sam_ads_free_private_data(void **vp) +{ + SAM_ADS_PRIVATES **sam_ads_state = (SAM_ADS_PRIVATES **)vp; + + if ((*sam_ads_state)->ads_struct->ld) { + ldap_unbind((*sam_ads_state)->ads_struct->ld); + } + + ads_destroy(&((*sam_ads_state)->ads_struct)); + + talloc_destroy((*sam_ads_state)->mem_ctx); + FIXME("maybe we must free some other stuff here\n"); + + *sam_ads_state = NULL; +} + + + +/***************************************************** +Init the ADS SAM backend +******************************************************/ +NTSTATUS sam_init_ads(SAM_METHODS *sam_method, const char *module_params) +{ + ADS_STATUS ads_status; + SAM_ADS_PRIVATES *sam_ads_state; + TALLOC_CTX *mem_ctx; + + SAM_ASSERT(sam_method && sam_method->parent); + + mem_ctx = sam_method->parent->mem_ctx; + + /* Here the SAM API functions of the sam_ads module */ + + /* General API */ + + sam_method->sam_get_sec_desc = sam_ads_get_sec_desc; + sam_method->sam_set_sec_desc = sam_ads_set_sec_desc; + + sam_method->sam_lookup_sid = sam_ads_lookup_sid; + sam_method->sam_lookup_name = sam_ads_lookup_name; + + /* Domain API */ + + sam_method->sam_update_domain = sam_ads_update_domain; + sam_method->sam_get_domain_handle = sam_ads_get_domain_handle; + + /* Account API */ + + sam_method->sam_create_account = sam_ads_create_account; + sam_method->sam_add_account = sam_ads_add_account; + sam_method->sam_update_account = sam_ads_update_account; + sam_method->sam_delete_account = sam_ads_delete_account; + sam_method->sam_enum_accounts = sam_ads_enum_accounts; + + sam_method->sam_get_account_by_sid = sam_ads_get_account_by_sid; + sam_method->sam_get_account_by_name = sam_ads_get_account_by_name; + + /* Group API */ + + sam_method->sam_create_group = sam_ads_create_group; + sam_method->sam_add_group = sam_ads_add_group; + sam_method->sam_update_group = sam_ads_update_group; + sam_method->sam_delete_group = sam_ads_delete_group; + sam_method->sam_enum_groups = sam_ads_enum_groups; + sam_method->sam_get_group_by_sid = sam_ads_get_group_by_sid; + sam_method->sam_get_group_by_name = sam_ads_get_group_by_name; + + sam_method->sam_add_member_to_group = sam_ads_add_member_to_group; + sam_method->sam_delete_member_from_group = sam_ads_delete_member_from_group; + sam_method->sam_enum_groupmembers = sam_ads_enum_groupmembers; + + sam_method->sam_get_groups_of_sid = sam_ads_get_groups_of_sid; + + sam_ads_state = talloc_zero(mem_ctx, sizeof(SAM_ADS_PRIVATES)); + if (!sam_ads_state) { + DEBUG(0, ("talloc() failed for sam_ads private_data!\n")); + return NT_STATUS_NO_MEMORY; + } + + if (!(sam_ads_state->mem_ctx = talloc_init("sam_ads_method"))) { + DEBUG(0, ("talloc_init() failed for sam_ads_state->mem_ctx\n")); + return NT_STATUS_NO_MEMORY; + } + + sam_ads_state->ads_bind_dn = talloc_strdup(sam_ads_state->mem_ctx, lp_parm_string(NULL,"sam_ads","bind as")); + sam_ads_state->ads_bind_pw = talloc_strdup(sam_ads_state->mem_ctx, lp_parm_string(NULL,"sam_ads","bind pw")); + + sam_ads_state->bind_plaintext = strequal(lp_parm_string(NULL, "sam_ads", "plaintext bind"), "yes"); + + if (!sam_ads_state->ads_bind_dn || !sam_ads_state->ads_bind_pw) { + DEBUG(0, ("talloc_strdup() failed for bind dn or password\n")); + return NT_STATUS_NO_MEMORY; + } + + /* Maybe we should not check the result here? Server down on startup? */ + + if (module_params && *module_params) { + sam_ads_state->ldap_uri = talloc_strdup(sam_ads_state->mem_ctx, module_params); + if (!sam_ads_state->ldap_uri) { + DEBUG(0, ("talloc_strdup() failed for bind dn or password\n")); + return NT_STATUS_NO_MEMORY; + } + } else { + sam_ads_state->ldap_uri = "ldapi://"; + } + + ads_status = sam_ads_cached_connection(sam_ads_state); + if (!ADS_ERR_OK(ads_status)) { + return ads_ntstatus(ads_status); + } + + sam_method->private_data = sam_ads_state; + sam_method->free_private_data = sam_ads_free_private_data; + + sam_ads_debug_level = debug_add_class("sam_ads"); + if (sam_ads_debug_level == -1) { + sam_ads_debug_level = DBGC_ALL; + DEBUG(0, ("sam_ads: Couldn't register custom debugging class!\n")); + } else DEBUG(2, ("sam_ads: Debug class number of 'sam_ads': %d\n", sam_ads_debug_level)); + + DEBUG(5, ("Initializing sam_ads\n")); + if (module_params) + DEBUG(10, ("Module Parameters for Domain %s[%s]: %s\n", sam_method->domain_name, sam_method->domain_name, module_params)); + return NT_STATUS_OK; +} + +#else /* HAVE_LDAP */ +void sam_ads_dummy(void) +{ + DEBUG(0,("sam_ads: not supported!\n")); +} +#endif /* HAVE_LDAP */ diff --git a/source4/sam/sam_plugin.c b/source4/sam/sam_plugin.c new file mode 100644 index 0000000000..fd26c4b8d3 --- /dev/null +++ b/source4/sam/sam_plugin.c @@ -0,0 +1,79 @@ +/* + Unix SMB/CIFS implementation. + Loadable san module interface. + Copyright (C) Jelmer Vernooij 2002 + Copyright (C) Andrew Bartlett 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_SAM + +NTSTATUS sam_init_plugin(SAM_METHODS *sam_methods, const char *module_params) +{ + void *dl_handle; + char *plugin_params, *plugin_name, *p; + sam_init_function plugin_init; + int (*plugin_version)(void); + + if (module_params == NULL) { + DEBUG(0, ("The plugin module needs an argument!\n")); + return NT_STATUS_UNSUCCESSFUL; + } + + plugin_name = smb_xstrdup(module_params); + p = strchr(plugin_name, ':'); + if (p) { + *p = 0; + plugin_params = p+1; + trim_string(plugin_params, " ", " "); + } else plugin_params = NULL; + trim_string(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, "sam_version"); + if (!plugin_version) { + sys_dlclose(dl_handle); + DEBUG(0, ("Failed to find function 'sam_version' using sys_dlsym in sam plugin %s (%s)\n", plugin_name, sys_dlerror())); + return NT_STATUS_UNSUCCESSFUL; + } + + if (plugin_version()!=SAM_INTERFACE_VERSION) { + sys_dlclose(dl_handle); + DEBUG(0, ("Wrong SAM_INTERFACE_VERSION! sam plugin has version %d and version %d is needed! Please update!\n", + plugin_version(),SAM_INTERFACE_VERSION)); + return NT_STATUS_UNSUCCESSFUL; + } + + plugin_init = sys_dlsym(dl_handle, "sam_init"); + if (!plugin_init) { + sys_dlclose(dl_handle); + DEBUG(0, ("Failed to find function 'sam_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 parameters %s for domain %s\n", plugin_name, plugin_params, sam_methods->domain_name)); + return plugin_init(sam_methods, plugin_params); +} diff --git a/source4/sam/sam_skel.c b/source4/sam/sam_skel.c new file mode 100644 index 0000000000..b4d64bb6da --- /dev/null +++ b/source4/sam/sam_skel.c @@ -0,0 +1,251 @@ +/* + Unix SMB/CIFS implementation. + this is a skeleton for SAM backend modules. + + Copyright (C) Stefan (metze) Metzmacher 2002 + 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" + +static int sam_skel_debug_level = DBGC_SAM; + +#undef DBGC_CLASS +#define DBGC_CLASS sam_skel_debug_level + +/* define the version of the SAM interface */ +SAM_MODULE_VERSIONING_MAGIC + +/* General API */ + +static NTSTATUS sam_skel_get_sec_desc(const SAM_METHODS *sam_methods, const NT_USER_TOKEN *access_token, const DOM_SID *sid, SEC_DESC **sd) +{ + DEBUG(0,("sam_skel: %s was called!\n",FUNCTION_MACRO)); + return NT_STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS sam_skel_set_sec_desc(const SAM_METHODS *sam_methods, const NT_USER_TOKEN *access_token, const DOM_SID *sid, const SEC_DESC *sd) +{ + DEBUG(0,("sam_skel: %s was called!\n",FUNCTION_MACRO)); + return NT_STATUS_NOT_IMPLEMENTED; +} + + +static NTSTATUS sam_skel_lookup_sid(const SAM_METHODS *sam_methods, const NT_USER_TOKEN *access_token, TALLOC_CTX *mem_ctx, const DOM_SID *sid, char **name, uint32 *type) +{ + DEBUG(0,("sam_skel: %s was called!\n",FUNCTION_MACRO)); + return NT_STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS sam_skel_lookup_name(const SAM_METHODS *sam_methods, const NT_USER_TOKEN *access_token, const char *name, DOM_SID *sid, uint32 *type) +{ + DEBUG(0,("sam_skel: %s was called!\n",FUNCTION_MACRO)); + return NT_STATUS_NOT_IMPLEMENTED; +} + + +/* Domain API */ + +static NTSTATUS sam_skel_update_domain(const SAM_METHODS *sam_methods, const SAM_DOMAIN_HANDLE *domain) +{ + DEBUG(0,("sam_skel: %s was called!\n",FUNCTION_MACRO)); + return NT_STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS sam_skel_get_domain_handle(const SAM_METHODS *sam_methods, const NT_USER_TOKEN *access_token, uint32 access_desired, SAM_DOMAIN_HANDLE **domain) +{ + DEBUG(0,("sam_skel: %s was called!\n",FUNCTION_MACRO)); + return NT_STATUS_NOT_IMPLEMENTED; +} + + +/* Account API */ + +static NTSTATUS sam_skel_create_account(const SAM_METHODS *sam_methods, const NT_USER_TOKEN *access_token, uint32 access_desired, const char *account_name, uint16 acct_ctrl, SAM_ACCOUNT_HANDLE **account) +{ + DEBUG(0,("sam_skel: %s was called!\n",FUNCTION_MACRO)); + return NT_STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS sam_skel_add_account(const SAM_METHODS *sam_methods, const SAM_ACCOUNT_HANDLE *account) +{ + DEBUG(0,("sam_skel: %s was called!\n",FUNCTION_MACRO)); + return NT_STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS sam_skel_update_account(const SAM_METHODS *sam_methods, const SAM_ACCOUNT_HANDLE *account) +{ + DEBUG(0,("sam_skel: %s was called!\n",FUNCTION_MACRO)); + return NT_STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS sam_skel_delete_account(const SAM_METHODS *sam_methods, const SAM_ACCOUNT_HANDLE *account) +{ + DEBUG(0,("sam_skel: %s was called!\n",FUNCTION_MACRO)); + return NT_STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS sam_skel_enum_accounts(const SAM_METHODS *sam_methods, const NT_USER_TOKEN *access_token, uint16 acct_ctrl, uint32 *account_count, SAM_ACCOUNT_ENUM **accounts) +{ + DEBUG(0,("sam_skel: %s was called!\n",FUNCTION_MACRO)); + return NT_STATUS_NOT_IMPLEMENTED; +} + + +static NTSTATUS sam_skel_get_account_by_sid(const SAM_METHODS *sam_methods, const NT_USER_TOKEN *access_token, uint32 access_desired, const DOM_SID *accountsid, SAM_ACCOUNT_HANDLE **account) +{ + DEBUG(0,("sam_skel: %s was called!\n",FUNCTION_MACRO)); + return NT_STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS sam_skel_get_account_by_name(const SAM_METHODS *sam_methods, const NT_USER_TOKEN *access_token, uint32 access_desired, const char *name, SAM_ACCOUNT_HANDLE **account) +{ + DEBUG(0,("sam_skel: %s was called!\n",FUNCTION_MACRO)); + return NT_STATUS_NOT_IMPLEMENTED; +} + + +/* Group API */ + +static NTSTATUS sam_skel_create_group(const SAM_METHODS *sam_methods, const NT_USER_TOKEN *access_token, uint32 access_desired, const char *account_name, uint16 group_ctrl, SAM_GROUP_HANDLE **group) +{ + DEBUG(0,("sam_skel: %s was called!\n",FUNCTION_MACRO)); + return NT_STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS sam_skel_add_group(const SAM_METHODS *sam_methods, const SAM_GROUP_HANDLE *group) +{ + DEBUG(0,("sam_skel: %s was called!\n",FUNCTION_MACRO)); + return NT_STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS sam_skel_update_group(const SAM_METHODS *sam_methods, const SAM_GROUP_HANDLE *group) +{ + DEBUG(0,("sam_skel: %s was called!\n",FUNCTION_MACRO)); + return NT_STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS sam_skel_delete_group(const SAM_METHODS *sam_methods, const SAM_GROUP_HANDLE *group) +{ + DEBUG(0,("sam_skel: %s was called!\n",FUNCTION_MACRO)); + return NT_STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS sam_skel_enum_groups(const SAM_METHODS *sam_methods, const NT_USER_TOKEN *access_token, uint16 group_ctrl, uint32 *groups_count, SAM_GROUP_ENUM **groups) +{ + DEBUG(0,("sam_skel: %s was called!\n",FUNCTION_MACRO)); + return NT_STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS sam_skel_get_group_by_sid(const SAM_METHODS *sam_methods, const NT_USER_TOKEN *access_token, uint32 access_desired, const DOM_SID *groupsid, SAM_GROUP_HANDLE **group) +{ + DEBUG(0,("sam_skel: %s was called!\n",FUNCTION_MACRO)); + return NT_STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS sam_skel_get_group_by_name(const SAM_METHODS *sam_methods, const NT_USER_TOKEN *access_token, uint32 access_desired, const char *name, SAM_GROUP_HANDLE **group) +{ + DEBUG(0,("sam_skel: %s was called!\n",FUNCTION_MACRO)); + return NT_STATUS_NOT_IMPLEMENTED; +} + + +static NTSTATUS sam_skel_add_member_to_group(const SAM_METHODS *sam_methods, const SAM_GROUP_HANDLE *group, const SAM_GROUP_MEMBER *member) +{ + DEBUG(0,("sam_skel: %s was called!\n",FUNCTION_MACRO)); + return NT_STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS sam_skel_delete_member_from_group(const SAM_METHODS *sam_methods, const SAM_GROUP_HANDLE *group, const SAM_GROUP_MEMBER *member) +{ + DEBUG(0,("sam_skel: %s was called!\n",FUNCTION_MACRO)); + return NT_STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS sam_skel_enum_groupmembers(const SAM_METHODS *sam_methods, const SAM_GROUP_HANDLE *group, uint32 *members_count, SAM_GROUP_MEMBER **members) +{ + DEBUG(0,("sam_skel: %s was called!\n",FUNCTION_MACRO)); + return NT_STATUS_NOT_IMPLEMENTED; +} + + +static NTSTATUS sam_skel_get_groups_of_sid(const SAM_METHODS *sam_methods, const NT_USER_TOKEN *access_token, const DOM_SID **sids, uint16 group_ctrl, uint32 *group_count, SAM_GROUP_ENUM **groups) +{ + DEBUG(0,("sam_skel: %s was called!\n",FUNCTION_MACRO)); + return NT_STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS sam_init_skel(SAM_METHODS *sam_methods, const char *module_params) +{ + /* Functions your SAM module doesn't provide should be set + * to NULL */ + + sam_methods->sam_get_sec_desc = sam_skel_get_sec_desc; + sam_methods->sam_set_sec_desc = sam_skel_set_sec_desc; + + sam_methods->sam_lookup_sid = sam_skel_lookup_sid; + sam_methods->sam_lookup_name = sam_skel_lookup_name; + + /* Domain API */ + + sam_methods->sam_update_domain = sam_skel_update_domain; + sam_methods->sam_get_domain_handle = sam_skel_get_domain_handle; + + /* Account API */ + + sam_methods->sam_create_account = sam_skel_create_account; + sam_methods->sam_add_account = sam_skel_add_account; + sam_methods->sam_update_account = sam_skel_update_account; + sam_methods->sam_delete_account = sam_skel_delete_account; + sam_methods->sam_enum_accounts = sam_skel_enum_accounts; + + sam_methods->sam_get_account_by_sid = sam_skel_get_account_by_sid; + sam_methods->sam_get_account_by_name = sam_skel_get_account_by_name; + + /* Group API */ + + sam_methods->sam_create_group = sam_skel_create_group; + sam_methods->sam_add_group = sam_skel_add_group; + sam_methods->sam_update_group = sam_skel_update_group; + sam_methods->sam_delete_group = sam_skel_delete_group; + sam_methods->sam_enum_groups = sam_skel_enum_groups; + sam_methods->sam_get_group_by_sid = sam_skel_get_group_by_sid; + sam_methods->sam_get_group_by_name = sam_skel_get_group_by_name; + + sam_methods->sam_add_member_to_group = sam_skel_add_member_to_group; + sam_methods->sam_delete_member_from_group = sam_skel_delete_member_from_group; + sam_methods->sam_enum_groupmembers = sam_skel_enum_groupmembers; + + sam_methods->sam_get_groups_of_sid = sam_skel_get_groups_of_sid; + + sam_methods->free_private_data = NULL; + + + sam_skel_debug_level = debug_add_class("sam_skel"); + if (sam_skel_debug_level == -1) { + sam_skel_debug_level = DBGC_SAM; + DEBUG(0, ("sam_skel: Couldn't register custom debugging class!\n")); + } else DEBUG(2, ("sam_skel: Debug class number of 'sam_skel': %d\n", sam_skel_debug_level)); + + if(module_params) + DEBUG(0, ("Starting 'sam_skel' with parameters '%s' for domain %s\n", module_params, sam_methods->domain_name)); + else + DEBUG(0, ("Starting 'sam_skel' for domain %s without paramters\n", sam_methods->domain_name)); + + return NT_STATUS_OK; +} diff --git a/source4/script/.cvsignore b/source4/script/.cvsignore new file mode 100644 index 0000000000..7a8114ecd7 --- /dev/null +++ b/source4/script/.cvsignore @@ -0,0 +1 @@ +findsmb diff --git a/source4/script/addtosmbpass b/source4/script/addtosmbpass new file mode 100644 index 0000000000..bc82851c52 --- /dev/null +++ b/source4/script/addtosmbpass @@ -0,0 +1,74 @@ +#!/usr/bin/awk -f +# edit the line above to point to your real location of awk interpreter + +# awk program for adding new entries in smbpasswd files +# arguments are account names to add; feed it an existent Samba password +# file on stdin, results will be written on stdout +# +# Michal Jaegermann, michal@ellpspace.math.ualberta.ca, 1995-11-09 + +BEGIN { + me = "addtosmbpass"; + count = ARGC; + FS = ":"; + + if (count == 1) { + print "Usage:", me, + "name1 [name2 ....] < smbpasswd.in > smbpasswd.out"; + ARGV[1] = "/dev/null"; + ARGC = 2; + exit; + } + + for(i = 1; i < count; i++) { + names[ARGV[i]] = " "; + delete ARGV[i]; + } +# sane awk should work simply with 'ARGC = 1', but not every awk +# implementation is sane - big sigh!! + ARGV[1] = "-"; + ARGC = 2; +# +# If you have ypmatch but is not RPC registered (some Linux systems +# for example) comment out the next line. +# "which ypmatch" | getline ypmatch; + if (1 != match(ypmatch, /^\//)) { + ypmatch = ""; + } + pwdf = "/etc/passwd"; +} +#check for names already present in input +{ + print $0; + for(name in names) { + if($1 == name) { + delete names[name]; + } + } +} +END { + fmt = "%s:%s:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:"; + fmt = fmt "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:[U ]:LCT-00000000:%s:\n"; + for(name in names) { + while ((getline < pwdf) > 0) { + if ($1 == name) { + printf(fmt, $1, $3, $5); + close(pwdf); + notfound = ""; + break; + } + notfound = "n"; + } + $0 = ""; + if (notfound && ypmatch) { +# try to find in NIS databases + command = ypmatch " " name " passwd"; + command | getline; + if (NF > 0) { + printf(fmt, $1, $3, $5); + } + close(command); + } + } +} + diff --git a/source4/script/build_env.sh b/source4/script/build_env.sh new file mode 100644 index 0000000000..0000759f16 --- /dev/null +++ b/source4/script/build_env.sh @@ -0,0 +1,35 @@ +#!/bin/sh + +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 < /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/source4/script/extract_allparms.sh b/source4/script/extract_allparms.sh new file mode 100755 index 0000000000..f16068b3fd --- /dev/null +++ b/source4/script/extract_allparms.sh @@ -0,0 +1,2 @@ +#!/bin/sh +grep '{".*P_[GL]' param/loadparm.c | sed -e 's/&.*$//g' -e 's/",.*P_LOCAL.*$/ S/' -e 's/",.*P_GLOBAL.*$/ G/' -e 's/^ .*{"//g' | sort -f diff --git a/source4/script/find_missing_doc.pl b/source4/script/find_missing_doc.pl new file mode 100755 index 0000000000..b27a405e4d --- /dev/null +++ b/source4/script/find_missing_doc.pl @@ -0,0 +1,90 @@ +#!/usr/bin/perl + +my $doc_file = "/docs/docbook/manpages/smb.conf.5.sgml"; +my $source_file = "/source/param/loadparm.c"; + +my %link,%doc,%param; + +# This one shouldn't be documented at all +$doc{-valid} = "FOUND"; + +$topdir = (shift @ARGV) or $topdir = "."; + +################################################## +# Reading links from manpage + +open(IN,$topdir.$doc_file); + +while() { + if( /([^<]*)<\/parameter><\/link><\/para><\/listitem>/g ){ + $link{$2} = $1; + $ref{$1} = $2; + } +} + +close(IN); + +################################################## +# Reading documentation from manpage + +open(IN,$topdir.$doc_file) || die("Can't open $topdir$doc_file"); + +while() { + if( /([^<]*?)([ ]*)\(.\)([ ]*)<\/term>/g ) { + $key = $1; + $value = $2; + $doc{$value} = $key; + + # There is a reference to this entry + if($ref{$key} eq $value){ + $ref{$key} = "FOUND"; + } else { + if($ref{$key}) { + print "$key should refer to $value, but refers to " . $ref{$key} . "\n"; + } else { + print "$key should refer to $value, but has no reference!\n"; + } + $ref{$key} = $value; + } + } +} + +close(IN); + +################################################# +# Reading entries from source code + +open(SOURCE,$topdir.$source_file) || die("Can't open $topdir$source_file"); + +while ($ln = ) { + last if $ln =~ m/^static\ struct\ parm_struct\ parm_table.*/; +} #burn through the preceding lines + +while ($ln = ) { + last if $ln =~ m/^\s*\}\;\s*$/; + #pull in the param names only + next if $ln =~ m/.*P_SEPARATOR.*/; + next unless $ln =~ /.*\"(.*)\".*/; + + if($doc{lc($1)}) { + $doc{lc($1)} = "FOUND"; + } else { + print "$1 is not documented!\n"; + } +} +close SOURCE; + +################################################## +# Trying to find missing references + +foreach (keys %ref) { + if($ref{$_} cmp "FOUND") { + print "$_ references to " . $ref{$_} . ", but " . $ref{$_} . " isn't an anchor!\n"; + } +} + +foreach (keys %doc) { + if($doc{$_} cmp "FOUND") { + print "$_ is documented but is not a configuration option!\n"; + } +} diff --git a/source4/script/findsmb.in b/source4/script/findsmb.in new file mode 100755 index 0000000000..6276bd3f39 --- /dev/null +++ b/source4/script/findsmb.in @@ -0,0 +1,152 @@ +#!@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 $_"; +} + +sub ipsort # do numeric sort on last field of IP address +{ + @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 '*'|") || + 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>.*$//,); + +# print header info + +print "\nIP 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|") || + die("Can't get nmb name list.\n"); + @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; + } + +# do an smbclient command on the netbios name. + + open(SMB,"$SAMBABIN/smbclient -N -L $name -I $ip -U% |") || + die("Can't do smbclient command.\n"); + @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> - /,@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/source4/script/findstatic.pl b/source4/script/findstatic.pl new file mode 100755 index 0000000000..43a4916435 --- /dev/null +++ b/source4/script/findstatic.pl @@ -0,0 +1,70 @@ +#!/usr/bin/perl -w +# find a list of fns and variables in the code that could be static +# usually called with something like this: +# findstatic.pl `find . -name "*.o"` +# Andrew Tridgell + +use strict; + +# use nm to find the symbols +my($saved_delim) = $/; +undef $/; +my($syms) = `nm -o @ARGV`; +$/ = $saved_delim; + +my(@lines) = split(/\n/s, $syms); + +my(%def); +my(%undef); +my(%stype); + +my(%typemap) = ( + "T" => "function", + "C" => "uninitialised variable", + "D" => "initialised variable" + ); + + +# parse the symbols into defined and undefined +for (my($i)=0; $i <= $#{@lines}; $i++) { + my($line) = $lines[$i]; + if ($line =~ /(.*):[a-f0-9]* ([TCD]) (.*)/) { + my($fname) = $1; + my($symbol) = $3; + push(@{$def{$fname}}, $symbol); + $stype{$symbol} = $2; + } + if ($line =~ /(.*):\s* U (.*)/) { + my($fname) = $1; + my($symbol) = $2; + push(@{$undef{$fname}}, $symbol); + } +} + +# look for defined symbols that are never referenced outside the place they +# are defined +foreach my $f (keys %def) { + print "Checking $f\n"; + my($found_one) = 0; + foreach my $s (@{$def{$f}}) { + my($found) = 0; + foreach my $f2 (keys %undef) { + if ($f2 ne $f) { + foreach my $s2 (@{$undef{$f2}}) { + if ($s2 eq $s) { + $found = 1; + $found_one = 1; + } + } + } + } + if ($found == 0) { + my($t) = $typemap{$stype{$s}}; + print " '$s' is unique to $f ($t)\n"; + } + } + if ($found_one == 0) { + print " all symbols in '$f' are unused (main program?)\n"; + } +} + diff --git a/source4/script/genstruct.pl b/source4/script/genstruct.pl new file mode 100755 index 0000000000..081b81f510 --- /dev/null +++ b/source4/script/genstruct.pl @@ -0,0 +1,298 @@ +#!/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 +# 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(struct parse_string *, const char *, unsigned);\n"; + print OFILE "int gen_parse_struct_$name(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(struct parse_string *p, const char *ptr, unsigned indent) { + return gen_dump_struct(pinfo_$name, p, ptr, indent); +} +int gen_parse_struct_$name(char *ptr, const char *str) { + return gen_parse_struct(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 + +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/source4/script/installbin.sh b/source4/script/installbin.sh new file mode 100644 index 0000000000..c2f34082dd --- /dev/null +++ b/source4/script/installbin.sh @@ -0,0 +1,40 @@ +#!/bin/sh + +INSTALLPERMS=$1 +BASEDIR=$2 +BINDIR=$3 +LIBDIR=$4 +VARDIR=$5 +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 + ln -sf $BINDIR/$p2 /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/source4/script/installdat.sh b/source4/script/installdat.sh new file mode 100755 index 0000000000..7ff88ac788 --- /dev/null +++ b/source4/script/installdat.sh @@ -0,0 +1,23 @@ +#!/bin/sh +#fist version March 2002, Herb Lewis + +DATDIR=$1 +SRCDIR=$2/ + +echo Installing dat files in $DATDIR + +for f in $SRCDIR/codepages/*.dat; do + FNAME=$DATDIR/`basename $f` + echo $FNAME + cp $f $FNAME || echo Cannot install $FNAME. Does $USER have privileges? + chmod 0644 $FNAME +done + +cat << EOF +====================================================================== +The dat files have been installed. +====================================================================== +EOF + +exit 0 + diff --git a/source4/script/installdirs.sh b/source4/script/installdirs.sh new file mode 100755 index 0000000000..9557b86d3b --- /dev/null +++ b/source4/script/installdirs.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +while ( test -n "$1" ); do + if [ ! -d $1 ]; then + mkdir -p $1 + fi + + if [ ! -d $1 ]; then + echo Failed to make directory $1 + exit 1 + fi + + shift; +done + + + diff --git a/source4/script/installman.sh b/source4/script/installman.sh new file mode 100644 index 0000000000..5b6bba69ed --- /dev/null +++ b/source4/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 +# modified to accomodate international man pages (inspired +# by Japanese edition's approach) + +MANDIR=$1 +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/source4/script/installmodules.sh b/source4/script/installmodules.sh new file mode 100644 index 0000000000..ec5691992d --- /dev/null +++ b/source4/script/installmodules.sh @@ -0,0 +1,36 @@ +#!/bin/sh + +INSTALLPERMS=$1 +BASEDIR=$2 +LIBDIR=$3 +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 + + +cat << EOF +====================================================================== +The modules are installed. You may uninstall the modules using the +command "make uninstallmodules" or "make uninstall" to uninstall +binaries, man pages, shell scripts and modules. +====================================================================== +EOF + +exit 0 diff --git a/source4/script/installscripts.sh b/source4/script/installscripts.sh new file mode 100644 index 0000000000..bff5423e7c --- /dev/null +++ b/source4/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=$2 + +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/source4/script/installswat.sh b/source4/script/installswat.sh new file mode 100644 index 0000000000..c66604cdb8 --- /dev/null +++ b/source4/script/installswat.sh @@ -0,0 +1,127 @@ +#!/bin/sh +#fist version March 1998, Andrew Tridgell + +SWATDIR=$1 +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 + 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 + 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 html documentation + +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 + +# Install "server-side" includes + +for f in $SRCDIR../swat/$ln/include/*.html; do + 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 Using Samba book + +if [ "x$BOOKDIR" != "x" ]; then + + # Create directories + + for d in $BOOKDIR $BOOKDIR/figs $BOOKDIR/gifs; 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 + + # 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 + + # Gifs + + for f in $SRCDIR../docs/htmldocs/using_samba/gifs/*.gif; do + FNAME=$BOOKDIR/gifs/`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/source4/script/makeunicodecasemap.awk b/source4/script/makeunicodecasemap.awk new file mode 100644 index 0000000000..8424b6c672 --- /dev/null +++ b/source4/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/source4/script/mkinstalldirs b/source4/script/mkinstalldirs new file mode 100755 index 0000000000..f945dbf2bc --- /dev/null +++ b/source4/script/mkinstalldirs @@ -0,0 +1,38 @@ +#! /bin/sh +# mkinstalldirs --- make directory hierarchy +# Author: Noah Friedman +# Created: 1993-05-16 +# Public domain + +errstatus=0 + +for file +do + set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'` + shift + + pathcomp= + for d + do + pathcomp="$pathcomp$d" + case "$pathcomp" in + -* ) pathcomp=./$pathcomp ;; + esac + + if test ! -d "$pathcomp"; then + echo "mkdir $pathcomp" 1>&2 + + mkdir "$pathcomp" || lasterr=$? + + if test ! -d "$pathcomp"; then + errstatus=$lasterr + fi + fi + + pathcomp="$pathcomp/" + done +done + +exit $errstatus + +# mkinstalldirs ends here diff --git a/source4/script/mknissmbpasswd.sh b/source4/script/mknissmbpasswd.sh new file mode 100755 index 0000000000..a94c963bdc --- /dev/null +++ b/source4/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/source4/script/mknissmbpwdtbl.sh b/source4/script/mknissmbpwdtbl.sh new file mode 100755 index 0000000000..a9b34ff9a7 --- /dev/null +++ b/source4/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/source4/script/mkproto.awk b/source4/script/mkproto.awk new file mode 100644 index 0000000000..999066ed7d --- /dev/null +++ b/source4/script/mkproto.awk @@ -0,0 +1,165 @@ +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; + } +} + +# special handling for code merge of TNG to head +/^#define OLD_NTDOMAIN 1/ { + printf "#if OLD_NTDOMAIN\n" +} +/^#undef OLD_NTDOMAIN/ { + printf "#endif\n" +} +/^#define NEW_NTDOMAIN 1/ { + printf "#if NEW_NTDOMAIN\n" +} +/^#undef NEW_NTDOMAIN/ { + printf "#endif\n" +} + +# 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/ ) { + 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|^NTTIME|^DEVICEMODE/ ) { + gotstart = 1; + } + + if(!gotstart) { + next; + } +} + + +/[(].*[)][ \t]*$/ { + printf "%s;\n",$0; + next; +} + +/[(]/ { + inheader=1; + printf "%s\n",$0; + next; +} + diff --git a/source4/script/mkproto.sh b/source4/script/mkproto.sh new file mode 100644 index 0000000000..2bf96c9b41 --- /dev/null +++ b/source4/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'`" + +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/source4/script/mksmbpasswd.sh b/source4/script/mksmbpasswd.sh new file mode 100644 index 0000000000..854e1bd1b5 --- /dev/null +++ b/source4/script/mksmbpasswd.sh @@ -0,0 +1,6 @@ +#!/bin/sh +awk 'BEGIN {FS=":" + printf("#\n# SMB password file.\n#\n") + } +{ printf( "%s:%s:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:[U ]:LCT-00000000:%s\n", $1, $3, $5) } +' diff --git a/source4/script/revert.sh b/source4/script/revert.sh new file mode 100644 index 0000000000..8df5fd2fbd --- /dev/null +++ b/source4/script/revert.sh @@ -0,0 +1,18 @@ +#!/bin/sh +BINDIR=$1 +shift + +for p in $*; do + p2=`basename $p` + if [ -f $BINDIR/$p2.old ]; then + echo Restoring $BINDIR/$p2.old + mv $BINDIR/$p2 $BINDIR/$p2.new + mv $BINDIR/$p2.old $BINDIR/$p2 + rm -f $BINDIR/$p2.new + else + echo Not restoring $p + fi +done + +exit 0 + diff --git a/source4/script/scancvslog.pl b/source4/script/scancvslog.pl new file mode 100755 index 0000000000..c39f9111c1 --- /dev/null +++ b/source4/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)) { + $_ = ; + chomp $_; + next if (not ($_)); + if (/^\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*/) { + next if ($#Entry == -1); + push(Entry,$_); + return @Entry; + } else { + push(Entry,$_); + } + } + } + return @Entry; +} diff --git a/source4/script/smbtar b/source4/script/smbtar new file mode 100644 index 0000000000..f062cba9f0 --- /dev/null +++ b/source4/script/smbtar @@ -0,0 +1,165 @@ +#!/bin/sh +# +# smbtar script - front end to smbclient +# +# Authors: Martin.Kraemer +# 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` [] [] +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 Specify PC Server $server + -p Specify PC Password $password + -x Specify PC Share $service + -X Exclude mode Include + -N File for date comparison `set -- $newer; echo $2` + -b Specify tape's blocksize `set -- $blocksize; echo $2` + -d

Specify a directory in share $cdcmd + -l Specify a Samba Log Level `set -- $log; echo $2` + -u Specify User Name $username + -t 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 -N $log -D "'$cdcmd'" ${clientargs} \ +-T${tarcmd}${tarargs} $blocksize $newer $tapefile '${1+"$@"}' $verbose diff --git a/source4/script/uninstallbin.sh b/source4/script/uninstallbin.sh new file mode 100644 index 0000000000..a8bbdea7af --- /dev/null +++ b/source4/script/uninstallbin.sh @@ -0,0 +1,42 @@ +#!/bin/sh +#4 July 96 Dan.Shearer@UniSA.edu.au + +INSTALLPERMS=$1 +BASEDIR=$2 +BINDIR=$3 +LIBDIR=$4 +VARDIR=$5 +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/source4/script/uninstallman.sh b/source4/script/uninstallman.sh new file mode 100644 index 0000000000..3126709831 --- /dev/null +++ b/source4/script/uninstallman.sh @@ -0,0 +1,37 @@ +#!/bin/sh +#4 July 96 Dan.Shearer@UniSA.edu.au +# +# 13 Aug 2001 Rafal Szczesniak +# modified to accomodate international man pages (inspired +# by Japanese edition's approach) + + +MANDIR=$1 +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/source4/script/uninstallmodules.sh b/source4/script/uninstallmodules.sh new file mode 100644 index 0000000000..30582a39fa --- /dev/null +++ b/source4/script/uninstallmodules.sh @@ -0,0 +1,37 @@ +#!/bin/sh +#4 July 96 Dan.Shearer@UniSA.edu.au + +INSTALLPERMS=$1 +BASEDIR=$2 +LIBDIR=$3 +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/source4/script/uninstallscripts.sh b/source4/script/uninstallscripts.sh new file mode 100644 index 0000000000..13104acedd --- /dev/null +++ b/source4/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=$2 + +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/source4/script/updatesmbpasswd.sh b/source4/script/updatesmbpasswd.sh new file mode 100644 index 0000000000..1d7e0d7332 --- /dev/null +++ b/source4/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/source4/smbadduser b/source4/smbadduser new file mode 100755 index 0000000000..9837413aeb --- /dev/null +++ b/source4/smbadduser @@ -0,0 +1,73 @@ +#!/bin/csh +# +# smbadduser - Written by Mike Zakharoff +# +unalias * +set path = ($path /usr/local/samba/bin) + +set smbpasswd = /usr/local/samba/private/smbpasswd +#set smbpasswd = /etc/samba/smbpasswd +set user_map = /usr/local/samba/lib/users.map +#set user_map = /etc/samba/smbusers +# +# 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/source4/smbd/.cvsignore b/source4/smbd/.cvsignore new file mode 100644 index 0000000000..5f2a5c4cf7 --- /dev/null +++ b/source4/smbd/.cvsignore @@ -0,0 +1,2 @@ +*.po +*.po32 diff --git a/source4/smbd/build_options.c b/source4/smbd/build_options.c new file mode 100644 index 0000000000..e450fee436 --- /dev/null +++ b/source4/smbd/build_options.c @@ -0,0 +1,535 @@ +/* + Unix SMB/CIFS implementation. + Build Options for Samba Suite + Copyright (C) Vance Lankhaar 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" +#include "build_env.h" +#include "dynconfig.h" + +static void output(BOOL screen, const char *format, ...) PRINTF_ATTRIBUTE(2,3); + +/* +#define OUTPUT(x) snprintf(outstring,sizeof(outstring),x); output(screen,outstring); +*/ +/**************************************************************************** +helper function for build_options +****************************************************************************/ +static void output(BOOL screen, const char *format, ...) +{ + char *ptr; + va_list ap; + + va_start(ap, format); + vasprintf(&ptr,format,ap); + va_end(ap); + + if (screen) { + d_printf("%s", ptr); + } else { + DEBUG(4,("%s", ptr)); + } + + SAFE_FREE(ptr); +} + +/**************************************************************************** +options set at build time for the samba suite +****************************************************************************/ +void build_options(BOOL screen) +{ + if ((DEBUGLEVEL < 4) && (!screen)) { + return; + } + +#ifdef _BUILD_ENV_H + /* Output information about the build environment */ + output(screen,"Build environment:\n"); + output(screen," Built by: %s@%s\n",BUILD_ENV_USER,BUILD_ENV_HOST); + output(screen," Built on: %s\n",BUILD_ENV_DATE); + + output(screen," Built using: %s\n",BUILD_ENV_COMPILER); + output(screen," Build host: %s\n",BUILD_ENV_UNAME); + output(screen," SRCDIR: %s\n",BUILD_ENV_SRCDIR); + output(screen," BUILDDIR: %s\n",BUILD_ENV_BUILDDIR); + + +#endif + + /* Output various options (most correspond to --with options) */ + output(screen,"\nBuild options:\n"); +#ifdef WITH_SMBWRAPPER + output(screen," WITH_SMBWRAPPER\n"); +#endif +#ifdef WITH_AFS + output(screen," WITH_AFS\n"); +#endif +#ifdef WITH_DFS + output(screen," WITH_DFS\n"); +#endif +#ifdef KRB4_AUTH + output(screen," KRB4_AUTH"); +#endif +#ifdef HAVE_KRB5 + output(screen," HAVE_KRB5"); +#endif +#ifdef HAVE_GSSAPI + output(screen," HAVE_GSSAPI"); +#endif +#ifdef HAVE_LDAP + output(screen," HAVE_LDAP"); +#endif +#ifdef WITH_AUTOMOUNT + output(screen," WITH_AUTOMOUNT\n"); +#endif +#ifdef WITH_SMBMOUNT + output(screen," WITH_SMBMOUNT\n"); +#endif +#ifdef WITH_PAM + output(screen," WITH_PAM\n"); +#endif +#ifdef WITH_TDB_SAM + output(screen," WITH_TDB_SAM\n"); +#endif +#ifdef WITH_SMBPASSWD_SAM + output(screen," WITH_SMBPASSWD_SAM\n"); +#endif +#ifdef WITH_NISPLUS_SAM + output(screen," WITH_NISPLUS_SAM\n"); +#endif +#ifdef WITH_NISPLUS_HOME + output(screen," WITH_NISPLUS_HOME\n"); +#endif +#ifdef WITH_SYSLOG + output(screen," WITH_SYSLOG\n"); +#endif +#ifdef WITH_QUOTAS + output(screen," WITH_QUOTAS\n"); +#endif +#ifdef WITH_VFS + output(screen," WITH_VFS\n"); +#endif +#ifdef USE_SPINLOCKS + output(screen," USE_SPINLOCKS\n"); +#endif +#ifdef SPARC_SPINLOCKS + output(screen," SPARC_SPINLOCKS\n"); +#endif +#ifdef INTEL_SPINLOCKS + output(screen," INTEL_SPINLOCKS\n"); +#endif +#ifdef MIPS_SPINLOCKS + output(screen," MIPS_SPINLOCKS\n"); +#endif +#ifdef POWERPC_SPINLOCKS + output(screen," POWERPC_SPINLOCKS\n"); +#endif +#ifdef HAVE_UNIXWARE_ACLS + output(screen," HAVE_UNIXWARE_ACLS\n"); +#endif +#ifdef HAVE_SOLARIS_ACLS + output(screen," HAVE_SOLARIS_ACLS\n"); +#endif +#ifdef HAVE_IRIX_ACLS + output(screen," HAVE_IRIX_ACLS\n"); +#endif +#ifdef HAVE_AIX_ACLS + output(screen," HAVE_AIX_ACLS\n"); +#endif +#ifdef HAVE_POSIX_ACLS + output(screen," HAVE_POSIX_ACLS\n"); +#endif +#ifdef HAVE_TRU64_ACLS + output(screen," HAVE_TRU64_ACLS\n"); +#endif + +#ifdef HAVE_ACL_GET_PERM_NP + output(screen," HAVE_ACL_GET_PERM_NP\n"); +#endif +#ifdef HAVE_NO_ACLS + output(screen," HAVE_NO_ACLS\n"); +#endif +#ifdef HAVE_LIBREADLINE + output(screen," HAVE_LIBREADLINE\n"); +#endif +#ifdef WITH_LIBICONV + output(screen," WITH_LIBICONV: %s\n",WITH_LIBICONV); +#endif + + + /* Output various paths to files and directories */ + output(screen,"\nPaths:\n"); + output(screen," CONFIGFILE: %s\n", dyn_CONFIGFILE); +#ifdef PRIVATE_DIR + output(screen," PRIVATE_DIR: %s\n",PRIVATE_DIR); +#endif +#ifdef LMHOSTSFILE + output(screen," LMHOSTSFILE: %s\n",LMHOSTSFILE); +#endif + output(screen," SBINDIR: %s\n", dyn_SBINDIR); + output(screen," BINDIR: %s\n", dyn_BINDIR); + output(screen," LOCKDIR: %s\n",dyn_LOCKDIR); + output(screen," LOGFILEBASE: %s\n", dyn_LOGFILEBASE); + + /*Output various other options (most map to defines in the configure script*/ + output(screen,"\nOther Build Options:\n"); +#ifdef HAVE_VOLATILE + output(screen," HAVE_VOLATILE\n"); +#endif +#ifdef HAVE_SHADOW_H + output(screen," HAVE_SHADOW_H\n"); +#endif +#ifdef HAVE_CRYPT + output(screen," HAVE_CRYPT\n"); +#endif +#ifdef USE_BOTH_CRYPT_CALLS + output(screen," USE_BOTH_CRYPT_CALLS\n"); +#endif +#ifdef HAVE_TRUNCATED_SALT + output(screen," HAVE_TRUNCATED_SALT\n"); +#endif +#ifdef HAVE_CUPS + output(screen," HAVE_CUPS\n"); +#endif +#ifdef HAVE_CUPS_CUPS_H + output(screen," HAVE_CUPS_CUPS_H\n"); +#endif +#ifdef HAVE_CUPS_LANGUAGE_H + output(screen," HAVE_CUPS_LANGUAGE_H\n"); +#endif +#ifdef HAVE_DLOPEN + output(screen," HAVE_DLOPEN\n"); +#endif +#ifdef HAVE_DLCLOSE + output(screen," HAVE_DLCLOSE\n"); +#endif +#ifdef HAVE_DLSYM + output(screen," HAVE_DLSYM\n"); +#endif +#ifdef HAVE_DLERROR + output(screen," HAVE_DLERROR\n"); +#endif +#ifdef HAVE_UNIXSOCKET + output(screen," HAVE_UNIXSOCKET\n"); +#endif +#ifdef HAVE_SOCKLEN_T_TYPE + output(screen," HAVE_SOCKLEN_T_TYPE\n"); +#endif +#ifdef HAVE_SIG_ATOMIC_T_TYPE + output(screen," HAVE_SIG_ATOMIC_T_TYPE\n"); +#endif +#ifdef HAVE_SETRESUID + output(screen," HAVE_SETRESUID\n"); +#endif +#ifdef HAVE_SETRESGID + output(screen," HAVE_SETRESGID\n"); +#endif +#ifdef HAVE_CONNECT + output(screen," HAVE_CONNECT\n"); +#endif +#ifdef HAVE_YP_GET_DEFAULT_DOMAIN + output(screen," HAVE_YP_GET_DEFAULT_DOMAIN\n"); +#endif +#ifdef HAVE_STAT64 + output(screen," HAVE_STAT64\n"); +#endif +#ifdef HAVE_LSTAT64 + output(screen," HAVE_LSTAT64\n"); +#endif +#ifdef HAVE_FSTAT64 + output(screen," HAVE_FSTAT64\n"); +#endif +#ifdef HAVE_STRCASECMP + output(screen," HAVE_STRCASECMP\n"); +#endif +#ifdef HAVE_MEMSET + output(screen," HAVE_MEMSET\n"); +#endif +#ifdef HAVE_LONGLONG + output(screen," HAVE_LONGLONG\n"); +#endif +#ifdef COMPILER_SUPPORTS_LL + output(screen," COMPILER_SUPPORTS_LL\n"); +#endif +#ifdef SIZEOF_OFF_T + output(screen," SIZEOF_OFF_T: %d\n",SIZEOF_OFF_T); +#endif +#ifdef HAVE_OFF64_T + output(screen," HAVE_OFF64_T\n"); +#endif +#ifdef SIZEOF_INO_T + output(screen," SIZEOF_INO_T: %d\n",SIZEOF_INO_T); +#endif +#ifdef HAVE_INO64_T + output(screen," HAVE_INO64_T\n"); +#endif +#ifdef HAVE_STRUCT_DIRENT64 + output(screen," HAVE_STRUCT_DIRENT64\n"); +#endif +#ifdef HAVE_UNSIGNED_CHAR + output(screen," HAVE_UNSIGNED_CHAR\n"); +#endif +#ifdef HAVE_SOCK_SIN_LEN + output(screen," HAVE_SOCK_SIN_LEN\n"); +#endif +#ifdef SEEKDIR_RETURNS_VOID + output(screen," SEEKDIR_RETURNS_VOID\n"); +#endif +#ifdef HAVE_FUNCTION_MACRO + output(screen," HAVE_FUNCTION_MACRO\n"); +#endif +#ifdef HAVE_GETTIMEOFDAY + output(screen," HAVE_GETTIMEOFDAY\n"); +#endif +#ifdef HAVE_C99_VSNPRINTF + output(screen," HAVE_C99_VSNPRINTF\n"); +#endif +#ifdef HAVE_BROKEN_READDIR + output(screen," HAVE_BROKEN_READDIR\n"); +#endif +#ifdef HAVE_NATIVE_ICONV + output(screen," HAVE_NATIVE_ICONV\n"); +#endif +#ifdef HAVE_KERNEL_OPLOCKS_LINUX + output(screen," HAVE_KERNEL_OPLOCKS_LINUX\n"); +#endif +#ifdef HAVE_KERNEL_CHANGE_NOTIFY + output(screen," HAVE_KERNEL_CHANGE_NOTIFY\n"); +#endif +#ifdef HAVE_KERNEL_SHARE_MODES + output(screen," HAVE_KERNEL_SHARE_MODES\n"); +#endif +#ifdef HAVE_KERNEL_OPLOCKS_IRIX + output(screen," HAVE_KERNEL_OPLOCKS_IRIX\n"); +#endif +#ifdef HAVE_IRIX_SPECIFIC_CAPABILITIES + output(screen," HAVE_IRIX_SPECIFIC_CAPABILITIES\n"); +#endif +#ifdef HAVE_INT16_FROM_RPC_RPC_H + output(screen," HAVE_INT16_FROM_RPC_RPC_H\n"); +#endif +#ifdef HAVE_UINT16_FROM_RPC_RPC_H + output(screen," HAVE_UINT16_FROM_RPC_RPC_H\n"); +#endif +#ifdef HAVE_INT32_FROM_RPC_RPC_H + output(screen," HAVE_INT16_FROM_RPC_RPC_H\n"); +#endif +#ifdef HAVE_UINT32_FROM_RPC_RPC_H + output(screen," HAVE_UINT32_FROM_RPC_RPC_H\n"); +#endif +#ifdef HAVE_RPC_AUTH_ERROR_CONFLICT + output(screen," HAVE_RPC_AUTH_ERROR_CONFLICT\n"); +#endif +#ifdef HAVE_FTRUNCATE_EXTEND + output(screen," HAVE_FTRUNCATE_EXTEND\n"); +#endif +#ifdef HAVE_WORKING_AF_LOCAL + output(screen," HAVE_WORKING_AF_LOCAL\n"); +#endif +#ifdef HAVE_BROKEN_GETGROUPS + output(screen," HAVE_BROKEN_GETGROUPS\n"); +#endif +#ifdef REPLACE_GETPASS + output(screen," REPLACE_GETPASS\n"); +#endif +#ifdef REPLACE_INET_NTOA + output(screen," REPLACE_INET_NTOA\n"); +#endif +#ifdef HAVE_SECURE_MKSTEMP + output(screen," HAVE_SECURE_MKSTEMP\n"); +#endif +#ifdef SYSCONF_SC_NGROUPS_MAX + output(screen," SYSCONF_SC_NGROUPS_MAX\n"); +#endif +#ifdef HAVE_IFACE_AIX + output(screen," HAVE_IFACE_AIX\n"); +#endif +#ifdef HAVE_IFACE_IFCONF + output(screen," HAVE_IFACE_IFCONF\n"); +#endif +#ifdef HAVE_IFACE_IFREQ + output(screen," HAVE_IFACE_IFREQ\n"); +#endif +#ifdef USE_SETRESUID + output(screen," USE_SETRESUID\n"); +#endif +#ifdef USE_SETRESGID + output(screen," USE_SETREUID\n"); +#endif +#ifdef USE_SETEUID + output(screen," USE_SETEUID\n"); +#endif +#ifdef USE_SETUIDX + output(screen," USE_SETUIDX\n"); +#endif +#ifdef HAVE_MMAP + output(screen," HAVE_MMAP\n"); +#endif +#ifdef MMAP_BLACKLIST + output(screen," MMAP_BLACKLIST\n"); +#endif +#ifdef FTRUNCATE_NEEDS_ROOT + output(screen," FTRUNCATE_NEEDS_ROOT\n"); +#endif +#ifdef HAVE_FCNTL_LOCK + output(screen," HAVE_FCNTL_LOCK\n"); +#endif +#ifdef HAVE_BROKEN_FCNTL64_LOCKS + output(screen," HAVE_BROKEN_FCNTL64_LOCKS\n"); +#endif +#ifdef HAVE_STRUCT_FLOCK64 + output(screen," HAVE_STRUCT_FLOCK64\n"); +#endif +#ifdef BROKEN_NISPLUS_INCLUDE_FILES + output(screen," BROKEN_NISPLUS_INCLUDE_FILES\n"); +#endif +#ifdef HAVE_LIBPAM + output(screen," HAVE_LIBPAM\n"); +#endif +#ifdef STAT_STATVFS64 + output(screen," STAT_STATVFS64\n"); +#endif +#ifdef STAT_STATVFS + output(screen," STAT_STATVFS\n"); +#endif +#ifdef STAT_STATFS3_OSF1 + output(screen," STAT_STATFS3_OSF1\n"); +#endif +#ifdef STAT_STATFS2_BSIZE + output(screen," STAT_STATFS2_BSIZE\n"); +#endif +#ifdef STAT_STATFS4 + output(screen," STAT_STATFS4\n"); +#endif +#ifdef STAT_STATFS2_FSIZE + output(screen," STAT_STATFS2_FSIZE\n"); +#endif +#ifdef STAT_STATFS2_FS_DATA + output(screen," STAT_STATFS2_FS_DATA\n"); +#endif +#ifdef HAVE_EXPLICIT_LARGEFILE_SUPPORT + output(screen," HAVE_EXPLICIT_LARGEFILE_SUPPORT\n"); +#endif + +#ifdef WITH_UTMP + /* Output UTMP Stuff */ + output(screen,"\nUTMP Related:\n"); + output(screen," WITH_UTMP\n"); + +#ifdef HAVE_UTIMBUF + output(screen," HAVE_UTIMBUF\n"); +#endif +#ifdef HAVE_UT_UT_NAME + output(screen," HAVE_UT_UT_NAME\n"); +#endif +#ifdef HAVE_UT_UT_USER + output(screen," HAVE_UT_UT_USER\n"); +#endif +#ifdef HAVE_UT_UT_ID + output(screen," HAVE_UT_UT_ID\n"); +#endif +#ifdef HAVE_UT_UT_HOST + output(screen," HAVE_UT_UT_HOST\n"); +#endif +#ifdef HAVE_UT_UT_TIME + output(screen," HAVE_UT_UT_TIME\n"); +#endif +#ifdef HAVE_UT_UT_TV + output(screen," HAVE_UT_UT_TV\n"); +#endif +#ifdef HAVE_UT_UT_TYPE + output(screen," HAVE_UT_UT_TYPE\n"); +#endif +#ifdef HAVE_UT_UT_PID + output(screen," HAVE_UT_UT_PID\n"); +#endif +#ifdef HAVE_UT_UT_EXIT + output(screen," HAVE_UT_UT_EXIT\n"); +#endif +#ifdef HAVE_UT_UT_ADDR + output(screen," HAVE_UT_UT_ADDR\n"); +#endif +#ifdef PUTUTLINE_RETURNS_UTMP + output(screen," PUTUTLINE_RETURNS_UTMP\n"); +#endif +#ifdef HAVE_UX_UT_SYSLEN + output(screen," HAVE_UX_UT_SYSLEN\n"); +#endif +#endif /* WITH_UTMP */ + + /* Output Build OS */ + output(screen,"\nBuilt for host os:\n"); +#ifdef LINUX + output(screen," LINUX\n"); +#endif +#ifdef SUNOS5 + output(screen," SUNOS5\n"); +#endif +#ifdef SUNOS4 + output(screen," SUNOS4\n"); +#endif + /* BSD Isn't Defined in the configure script, but there is something about it in include/config.h.in (and I guess acconfig.h) */ +#ifdef BSD + output(screen," BSD\n"); +#endif +#ifdef IRIX + output(screen," IRIX\n"); +#endif +#ifdef IRIX6 + output(screen," IRIX6\n"); +#endif +#ifdef AIX + output(screen," AIX\n"); +#endif +#ifdef HPUX + output(screen," HPUX\n"); +#endif +#ifdef QNX + output(screen," QNX\n"); +#endif +#ifdef OSF1 + output(screen," OSF1\n"); +#endif +#ifdef SCO + output(screen," SCO\n"); +#endif +#ifdef UNIXWARE + output(screen," UNIXWARE\n"); +#endif +#ifdef NEXT2 + output(screen," NEXT2\n"); +#endif +#ifdef RELIANTUNIX + output(screen," RELIANTUNIX\n"); +#endif + + /* Output the sizes of the various types */ + output(screen,"\nType sizes:\n"); + output(screen," sizeof(char): %d\n",sizeof(char)); + output(screen," sizeof(int): %d\n",sizeof(int)); + output(screen," sizeof(long): %d\n",sizeof(long)); + output(screen," sizeof(uint8): %d\n",sizeof(uint8)); + output(screen," sizeof(uint16): %d\n",sizeof(uint16)); + output(screen," sizeof(uint32): %d\n",sizeof(uint32)); + output(screen," sizeof(short): %d\n",sizeof(short)); + output(screen," sizeof(void*): %d\n",sizeof(void*)); +} + + + diff --git a/source4/smbd/conn.c b/source4/smbd/conn.c new file mode 100644 index 0000000000..0498b3cb5d --- /dev/null +++ b/source4/smbd/conn.c @@ -0,0 +1,158 @@ +/* + 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" + +/* set these to define the limits of the server. NOTE These are on a + per-client basis. Thus any one machine can't connect to more than + MAX_CONNECTIONS services, but any number of machines may connect at + one time. */ +#define MAX_CONNECTIONS 128 + +/**************************************************************************** +init the conn structures +****************************************************************************/ +void conn_init(struct server_context *smb) +{ + smb->tree.bmap = bitmap_allocate(MAX_CONNECTIONS); +} + +/**************************************************************************** +check if a snum is in use +****************************************************************************/ +BOOL conn_snum_used(struct server_context *smb, int snum) +{ + struct tcon_context *conn; + for (conn=smb->tree.connections;conn;conn=conn->next) { + if (conn->service == snum) { + return(True); + } + } + return(False); +} + + +/**************************************************************************** +find a conn given a cnum +****************************************************************************/ +struct tcon_context *conn_find(struct server_context *smb, unsigned cnum) +{ + int count=0; + struct tcon_context *conn; + + for (conn=smb->tree.connections;conn;conn=conn->next,count++) { + if (conn->cnum == cnum) { + if (count > 10) { + DLIST_PROMOTE(smb->tree.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. +****************************************************************************/ +struct tcon_context *conn_new(struct server_context *smb) +{ + TALLOC_CTX *mem_ctx; + struct tcon_context *conn; + int i; + + i = bitmap_find(smb->tree.bmap, 1); + + if (i == -1) { + DEBUG(1,("ERROR! Out of connection structures\n")); + return NULL; + } + + mem_ctx = talloc_init("tcon_context[%d]", i); + + conn = (struct tcon_context *)talloc(mem_ctx, sizeof(*conn)); + if (!conn) return NULL; + + ZERO_STRUCTP(conn); + + conn->mem_ctx = mem_ctx; + conn->cnum = i; + conn->smb = smb; + + bitmap_set(smb->tree.bmap, i); + + smb->tree.num_open++; + + DLIST_ADD(smb->tree.connections, conn); + + return conn; +} + +/**************************************************************************** +close all conn structures +****************************************************************************/ +void conn_close_all(struct server_context *smb) +{ + struct tcon_context *conn, *next; + for (conn=smb->tree.connections;conn;conn=next) { + next=conn->next; + close_cnum(conn); + } +} + + +#if REWRITE_REMOVED +/**************************************************************************** +clear a vuid out of the validity cache, and as the 'owner' of a connection. +****************************************************************************/ +void conn_clear_vuid_cache(struct server_context *smb, uint16 vuid) +{ + struct tcon_context *conn; + unsigned int i; + + for (conn=smb->tree.connections;conn;conn=conn->next) { + for (i=0;ivuid_cache.entries && i< VUID_CACHE_SIZE;i++) { + if (conn->vuid_cache.list[i] == vuid) { + conn->vuid_cache.list[i] = UID_FIELD_INVALID; + } + } + } +} +#endif + +/**************************************************************************** + Free a conn structure. +****************************************************************************/ + +void conn_free(struct server_context *smb, struct tcon_context *conn) +{ + DLIST_REMOVE(smb->tree.connections, conn); + + bitmap_clear(smb->tree.bmap, conn->cnum); + smb->tree.num_open--; + + ZERO_STRUCTP(conn); + SAFE_FREE(conn); +} + diff --git a/source4/smbd/connection.c b/source4/smbd/connection.c new file mode 100644 index 0000000000..35f5138956 --- /dev/null +++ b/source4/smbd/connection.c @@ -0,0 +1,223 @@ +/* + 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; + + +static void make_conn_key(struct tcon_context *conn, const char *name, TDB_DATA *pkbuf, struct connections_key *pkey) +{ + ZERO_STRUCTP(pkey); + pkey->pid = getpid(); + pkey->cnum = conn?conn->cnum:-1; + fstrcpy(pkey->name, name); + + pkbuf->dptr = (char *)pkey; + pkbuf->dsize = sizeof(*pkey); +} + +/**************************************************************************** + Delete a connection record. +****************************************************************************/ + +BOOL yield_connection(struct tcon_context *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(struct tcon_context *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_log(lock_path(conn->mem_ctx, "connections.tdb"), 0, TDB_CLEAR_IF_FIRST|TDB_DEFAULT, + O_RDWR | O_CREAT, 0644); + + if (!tdb) + return False; + + /* + * Enforce the max connections parameter. + */ + + if (max_connections > 0) { + struct count_stat cs; + + cs.mypid = 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 = getpid(); + crec.cnum = conn?conn->cnum:-1; + if (conn) { + crec.uid = -1; + crec.gid = -1; + StrnCpy(crec.name, + lp_servicename(SNUM(conn)),sizeof(crec.name)-1); + } + crec.start = time(NULL); + crec.bcast_msg_flags = msg_flags; + + StrnCpy(crec.machine,sub_get_remote_machine(),sizeof(crec.machine)-1); + StrnCpy(crec.addr,conn?conn->smb->socket.client_addr:"NONE",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/source4/smbd/negprot.c b/source4/smbd/negprot.c new file mode 100644 index 0000000000..caf3ce33b9 --- /dev/null +++ b/source4/smbd/negprot.c @@ -0,0 +1,526 @@ +/* + 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" + +/* initialise the auth_context for this server and return the cryptkey */ +static void get_challenge(struct server_context *smb, char buff[8]) +{ + NTSTATUS nt_status; + const uint8 *cryptkey; + + /* muliple negprots are not premitted */ + if (smb->negotiate.auth_context) { + DEBUG(3,("get challenge: is this a secondary negprot? auth_context is non-NULL!\n")); + smb_panic("secondary negprot"); + } + + DEBUG(10, ("get challenge: creating negprot_global_auth_context\n")); + + nt_status = make_auth_context_subsystem(&smb->negotiate.auth_context); + + if (!NT_STATUS_IS_OK(nt_status)) { + 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 = smb->negotiate.auth_context->get_ntlm_challenge(smb->negotiate.auth_context); + memcpy(buff, cryptkey, 8); +} + +/**************************************************************************** + Reply for the core protocol. +****************************************************************************/ +static void reply_corep(struct request_context *req, uint16 choice) +{ + req_setup_reply(req, 1, 0); + + SSVAL(req->out.vwv, VWV(0), choice); + + req->smb->negotiate.protocol = PROTOCOL_CORE; + + req_send_reply(req); +} + +/**************************************************************************** + Reply for the coreplus protocol. +this is quite incomplete - we only fill in a small part of the reply, but as nobody uses +this any more it probably doesn't matter +****************************************************************************/ +static void reply_coreplus(struct request_context *req, uint16 choice) +{ + uint16 raw = (lp_readraw()?1:0) | (lp_writeraw()?2:0); + + req_setup_reply(req, 13, 0); + + /* Reply, SMBlockread, SMBwritelock supported. */ + SCVAL(req->out.hdr,HDR_FLG, + CVAL(req->out.hdr, HDR_FLG) | FLAG_SUPPORT_LOCKREAD); + + SSVAL(req->out.vwv, VWV(0), choice); + SSVAL(req->out.vwv, VWV(1), 0x1); /* user level security, don't encrypt */ + + /* tell redirector we support + readbraw and writebraw (possibly) */ + SSVAL(req->out.vwv, VWV(5), raw); + + req->smb->negotiate.protocol = PROTOCOL_COREPLUS; + + req_send_reply(req); +} + +/**************************************************************************** + Reply for the lanman 1.0 protocol. +****************************************************************************/ +static void reply_lanman1(struct request_context *req, uint16 choice) +{ + int raw = (lp_readraw()?1:0) | (lp_writeraw()?2:0); + int secword=0; + time_t t = req->request_time.tv_sec; + + req->smb->negotiate.encrypted_passwords = lp_encrypted_passwords(); + + if (lp_security() != SEC_SHARE) + secword |= NEGOTIATE_SECURITY_USER_LEVEL; + + if (req->smb->negotiate.encrypted_passwords) + secword |= NEGOTIATE_SECURITY_CHALLENGE_RESPONSE; + + req->smb->negotiate.protocol = PROTOCOL_LANMAN1; + + req_setup_reply(req, 13, req->smb->negotiate.encrypted_passwords ? 8 : 0); + + /* SMBlockread, SMBwritelock supported. */ + SCVAL(req->out.hdr,HDR_FLG, + CVAL(req->out.hdr, HDR_FLG) | FLAG_SUPPORT_LOCKREAD); + + SSVAL(req->out.vwv, VWV(0), choice); + SSVAL(req->out.vwv, VWV(1), secword); + SSVAL(req->out.vwv, VWV(2), req->smb->negotiate.max_recv); + SSVAL(req->out.vwv, VWV(3), lp_maxmux()); + SSVAL(req->out.vwv, VWV(4), 1); + SSVAL(req->out.vwv, VWV(5), raw); + SIVAL(req->out.vwv, VWV(6), req->smb->pid); + put_dos_date(req->out.vwv, VWV(8), t); + SSVAL(req->out.vwv, VWV(10), TimeDiff(t)/60); + + /* Create a token value and add it to the outgoing packet. */ + if (req->smb->negotiate.encrypted_passwords) { + SSVAL(req->out.vwv, VWV(11), 8); + get_challenge(req->smb, req->out.data); + } + + req_send_reply(req); +} + +/**************************************************************************** + Reply for the lanman 2.0 protocol. +****************************************************************************/ +static void reply_lanman2(struct request_context *req, uint16 choice) +{ + int raw = (lp_readraw()?1:0) | (lp_writeraw()?2:0); + int secword=0; + time_t t = req->request_time.tv_sec; + + req->smb->negotiate.encrypted_passwords = lp_encrypted_passwords(); + + if (lp_security() != SEC_SHARE) + secword |= NEGOTIATE_SECURITY_USER_LEVEL; + + if (req->smb->negotiate.encrypted_passwords) + secword |= NEGOTIATE_SECURITY_CHALLENGE_RESPONSE; + + req->smb->negotiate.protocol = PROTOCOL_LANMAN2; + + req_setup_reply(req, 13, 0); + + SSVAL(req->out.vwv, VWV(0), choice); + SSVAL(req->out.vwv, VWV(1), secword); + SSVAL(req->out.vwv, VWV(2), req->smb->negotiate.max_recv); + SSVAL(req->out.vwv, VWV(3), lp_maxmux()); + SSVAL(req->out.vwv, VWV(4), 1); + SSVAL(req->out.vwv, VWV(5), raw); + SIVAL(req->out.vwv, VWV(6), req->smb->pid); + put_dos_date(req->out.vwv, VWV(8), t); + SSVAL(req->out.vwv, VWV(10), TimeDiff(t)/60); + + /* Create a token value and add it to the outgoing packet. */ + if (req->smb->negotiate.encrypted_passwords) { + SSVAL(req->out.vwv, VWV(11), 8); + req_grow_data(req, 8); + get_challenge(req->smb, req->out.data); + } + + req_push_str(req, NULL, lp_workgroup(), -1, STR_TERMINATE); + + req_send_reply(req); +} + + +#if 0 +/**************************************************************************** + Generate the spnego negprot reply blob. Return the number of bytes used. +****************************************************************************/ +static DATA_BLOB negprot_spnego(struct server_context *smb) +{ + DATA_BLOB blob; + uint8 guid[16]; + const char *OIDs_krb5[] = {OID_KERBEROS5, + OID_KERBEROS5_OLD, + OID_NTLMSSP, + NULL}; + const char *OIDs_plain[] = {OID_NTLMSSP, NULL}; + char *principal; + + smb->negotiate.spnego_negotiated = True; + + memset(guid, 0, 16); + safe_strcpy((char *)guid, lp_netbios_name(), 16); + strlower((char *)guid); + +#if 0 + /* strangely enough, NT does not send 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); + } + + return blob; +} +#endif + +/**************************************************************************** + Reply for the nt protocol. +****************************************************************************/ +static void reply_nt1(struct request_context *req, uint16 choice) +{ + /* dual names + lock_and_read + nt SMBs + remote API calls */ + int capabilities; + int secword=0; + time_t t = req->request_time.tv_sec; + BOOL negotiate_spnego = False; + + capabilities = + CAP_NT_FIND | CAP_LOCK_AND_READ | + CAP_LEVEL_II_OPLOCKS | CAP_NT_SMBS | CAP_RPC_REMOTE_APIS; + + req->smb->negotiate.encrypted_passwords = lp_encrypted_passwords(); + + /* do spnego in user level security if the client + supports it and we can do encrypted passwords */ + + if (req->smb->negotiate.encrypted_passwords && + (lp_security() != SEC_SHARE) && + lp_use_spnego() && + (req->flags2 & FLAGS2_EXTENDED_SECURITY)) { +/* REWRITE negotiate_spnego = True; + capabilities |= CAP_EXTENDED_SECURITY; +*/ + } + + 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_SHARE) { + secword |= NEGOTIATE_SECURITY_USER_LEVEL; + } + + if (req->smb->negotiate.encrypted_passwords) { + secword |= NEGOTIATE_SECURITY_CHALLENGE_RESPONSE; + } + + req->smb->negotiate.protocol = PROTOCOL_NT1; + + req_setup_reply(req, 17, 0); + + SSVAL(req->out.vwv, VWV(0), choice); + SCVAL(req->out.vwv, VWV(1), secword); + + /* notice the strange +1 on vwv here? That's because + this is the one and only SMB packet that is malformed in + the specification - all the command words after the secword + are offset by 1 byte */ + SSVAL(req->out.vwv+1, VWV(1), lp_maxmux()); + SSVAL(req->out.vwv+1, VWV(2), 1); /* num vcs */ + SIVAL(req->out.vwv+1, VWV(3), req->smb->negotiate.max_recv); + SIVAL(req->out.vwv+1, VWV(5), 0x10000); /* raw size. full 64k */ + SIVAL(req->out.vwv+1, VWV(7), req->smb->pid); /* session key */ + SIVAL(req->out.vwv+1, VWV(9), capabilities); + put_long_date(req->out.vwv + VWV(11) + 1, t); + SSVALS(req->out.vwv+1,VWV(15), TimeDiff(t)/60); + + if (!negotiate_spnego) { + /* Create a token value and add it to the outgoing packet. */ + if (req->smb->negotiate.encrypted_passwords) { + req_grow_data(req, 8); + /* note that we do not send a challenge at all if + we are using plaintext */ + get_challenge(req->smb, req->out.ptr); + req->out.ptr += 8; + SCVAL(req->out.vwv+1, VWV(16), 8); + } + req_push_str(req, NULL, lp_workgroup(), -1, STR_UNICODE|STR_TERMINATE|STR_NOALIGN); + DEBUG(3,("not using SPNEGO\n")); + } else { +#if 0 + DATA_BLOB blob = negprot_spnego(req->smb); + + req_grow_data(req, blob.length); + memcpy(req->out.ptr, blob.data, blob.length); + DEBUG(3,("using SPNEGO\n")); +#else + exit_server(req->smb, "no SPNEGO please"); +#endif + } + + req_send_reply(req); +} + +/* 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; + void (*proto_reply_fn)(struct request_context *req, uint16 choice); + 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. +****************************************************************************/ + +void reply_negprot(struct request_context *req) +{ + int Index=0; + int choice = -1; + int protocol; + char *p; + int arch = ARCH_ALL; + + if (req->smb->negotiate.done_negprot) { + exit_server(req->smb, "multiple negprot's are not permitted"); + } + req->smb->negotiate.done_negprot = True; + + p = req->in.data + 1; + + while (p < req->in.data + req->in.data_size) { + Index++; + DEBUG(3,("Requested protocol [%s]\n",p)); + if (strcmp(p,"Windows for Workgroups 3.1a") == 0) + arch &= ( ARCH_WFWG | ARCH_WIN95 | ARCH_WINNT | ARCH_WIN2K ); + else if (strcmp(p,"DOS LM1.2X002") == 0) + arch &= ( ARCH_WFWG | ARCH_WIN95 ); + else if (strcmp(p,"DOS LANMAN2.1") == 0) + arch &= ( ARCH_WFWG | ARCH_WIN95 ); + else if (strcmp(p,"NT LM 0.12") == 0) + arch &= ( ARCH_WIN95 | ARCH_WINNT | ARCH_WIN2K ); + else if (strcmp(p,"LANMAN2.1") == 0) + arch &= ( ARCH_WINNT | ARCH_WIN2K | ARCH_OS2 ); + else if (strcmp(p,"LM1.2X002") == 0) + arch &= ( ARCH_WINNT | ARCH_WIN2K | ARCH_OS2 ); + else if (strcmp(p,"MICROSOFT NETWORKS 1.03") == 0) + arch &= ARCH_WINNT; + else if (strcmp(p,"XENIX CORE") == 0) + arch &= ( ARCH_WINNT | ARCH_OS2 ); + else if (strcmp(p,"Samba") == 0) { + arch = ARCH_SAMBA; + break; + } + + p += strlen(p) + 2; + } + + switch (arch) { + case ARCH_SAMBA: + set_remote_arch(req->smb, RA_SAMBA); + break; + case ARCH_WFWG: + set_remote_arch(req->smb, RA_WFWG); + break; + case ARCH_WIN95: + set_remote_arch(req->smb, RA_WIN95); + break; + case ARCH_WINNT: + if (req->flags2==FLAGS2_WIN2K_SIGNATURE) + set_remote_arch(req->smb, RA_WIN2K); + else + set_remote_arch(req->smb, RA_WINNT); + break; + case ARCH_WIN2K: + set_remote_arch(req->smb, RA_WIN2K); + break; + case ARCH_OS2: + set_remote_arch(req->smb, RA_OS2); + break; + default: + set_remote_arch(req->smb, RA_UNKNOWN); + break; + } + + /* possibly reload - change of architecture */ + reload_services(req->smb, True); + + /* Check for protocols, most desirable first */ + for (protocol = 0; supported_protocols[protocol].proto_name; protocol++) { + p = req->in.data+1; + Index = 0; + if ((supported_protocols[protocol].protocol_level <= lp_maxprotocol()) && + (supported_protocols[protocol].protocol_level >= lp_minprotocol())) + while (p < (req->in.data + req->in.data_size)) { + if (strequal(p,supported_protocols[protocol].proto_name)) + choice = Index; + Index++; + p += strlen(p) + 2; + } + if(choice != -1) + break; + } + + if(choice != -1) { + sub_set_remote_proto(supported_protocols[protocol].short_name); + reload_services(req->smb, True); + supported_protocols[protocol].proto_reply_fn(req, choice); + DEBUG(3,("Selected protocol %s\n",supported_protocols[protocol].proto_name)); + } else { + DEBUG(0,("No protocol supported !\n")); + } + + DEBUG(5,("negprot index=%d\n", choice)); +} diff --git a/source4/smbd/password.c b/source4/smbd/password.c new file mode 100644 index 0000000000..d2559ac41e --- /dev/null +++ b/source4/smbd/password.c @@ -0,0 +1,492 @@ +/* + 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" + + +/**************************************************************************** +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(struct server_context *smb, uint16 vuid) +{ + user_struct *usp; + int count=0; + + if (vuid == UID_FIELD_INVALID) + return NULL; + + for (usp=smb->users.validated_users;usp;usp=usp->next,count++) { + if (vuid == usp->vuid) { + if (count > 10) { + DLIST_PROMOTE(smb->users.validated_users, usp); + } + return usp; + } + } + + return NULL; +} + +/**************************************************************************** +invalidate a uid +****************************************************************************/ +void invalidate_vuid(struct server_context *smb, uint16 vuid) +{ + user_struct *vuser = get_valid_user_struct(smb, vuid); + + if (vuser == NULL) + return; + + SAFE_FREE(vuser->homedir); + SAFE_FREE(vuser->unix_homedir); + SAFE_FREE(vuser->logon_script); + + session_yield(vuser); + + free_server_info(&vuser->server_info); + + DLIST_REMOVE(smb->users.validated_users, vuser); + + /* clear the vuid from the 'cache' on each connection, and + from the vuid 'owner' of connections */ + /* REWRITE: conn_clear_vuid_cache(smb, vuid); */ + + SAFE_FREE(vuser->groups); + delete_nt_token(&vuser->nt_user_token); + SAFE_FREE(vuser); + smb->users.num_validated_vuids--; +} + +/**************************************************************************** +invalidate all vuid entries for this process +****************************************************************************/ +void invalidate_all_vuids(struct server_context *smb) +{ + user_struct *usp, *next=NULL; + + for (usp=smb->users.validated_users;usp;usp=next) { + next = usp->next; + + invalidate_vuid(smb, 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) + * + * @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(struct server_context *smb, + struct auth_serversupplied_info *server_info, + const char *smb_name) +{ + user_struct *vuser = NULL; + + /* Ensure no vuid gets registered in share level security. */ + if(lp_security() == SEC_SHARE) + return UID_FIELD_INVALID; + + /* Limit allowed vuids to 16bits - VUID_OFFSET. */ + if (smb->users.num_validated_vuids >= 0xFFFF-VUID_OFFSET) + return UID_FIELD_INVALID; + + if((vuser = (user_struct *)malloc( sizeof(user_struct) )) == NULL) { + DEBUG(0,("Failed to malloc users struct!\n")); + return UID_FIELD_INVALID; + } + + ZERO_STRUCTP(vuser); + + /* Allocate a free vuid. Yes this is a linear search... :-) */ + while (get_valid_user_struct(smb, smb->users.next_vuid) != NULL ) { + smb->users.next_vuid++; + /* Check for vuid wrap. */ + if (smb->users.next_vuid == UID_FIELD_INVALID) + smb->users.next_vuid = VUID_OFFSET; + } + + DEBUG(10,("register_vuid: allocated vuid = %u\n", + (unsigned int)smb->users.next_vuid)); + + vuser->vuid = smb->users.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 + */ + if (!IS_SAM_UNIX_USER(server_info->sam_account)) { + DEBUG(0,("Attempted session setup with invalid user. No uid/gid in SAM_ACCOUNT\n")); + free(vuser); + free_server_info(&server_info); + return UID_FIELD_INVALID; + } + + vuser->uid = pdb_get_uid(server_info->sam_account); + vuser->gid = pdb_get_gid(server_info->sam_account); + + 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")); + free(vuser); + free_server_info(&server_info); + return UID_FIELD_INVALID; + } + } + + vuser->guest = server_info->guest; + fstrcpy(vuser->user.unix_name, pdb_get_username(server_info->sam_account)); + + /* 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 *unix_homedir = pdb_get_unix_homedir(server_info->sam_account); + const char *logon_script = pdb_get_logon_script(server_info->sam_account); + if (homedir) { + vuser->homedir = smb_xstrdup(homedir); + } + + if (unix_homedir) { + vuser->unix_homedir = smb_xstrdup(unix_homedir); + } + + if (logon_script) { + vuser->logon_script = smb_xstrdup(logon_script); + } + } + + memcpy(vuser->session_key, server_info->session_key, sizeof(vuser->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); + SAFE_FREE(vuser->homedir); + SAFE_FREE(vuser->unix_homedir); + SAFE_FREE(vuser->logon_script); + + SAFE_FREE(vuser); + return UID_FIELD_INVALID; + } + + /* 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)); + + smb->users.next_vuid++; + smb->users.num_validated_vuids++; + + DLIST_ADD(smb->users.validated_users, vuser); + + if (!session_claim(smb, vuser)) { + DEBUG(1,("Failed to claim session for vuid=%d\n", vuser->vuid)); + invalidate_vuid(smb, vuser->vuid); + return -1; + } + + /* Register a home dir service for this user */ + if ((!vuser->guest) && vuser->unix_homedir && *(vuser->unix_homedir)) { + DEBUG(3, ("Adding/updating homes service for user '%s' using home direcotry: '%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; + } + + return vuser->vuid; +} + + +/**************************************************************************** +add a name to the session users list +****************************************************************************/ +void add_session_user(struct server_context *smb, const char *user) +{ + char *suser; + struct passwd *passwd; + + if (!(passwd = Get_Pwnam(user))) return; + + suser = strdup(passwd->pw_name); + if (!suser) { + return; + } + + if (suser && *suser && !in_list(suser,smb->users.session_users,False)) { + char *p; + if (!smb->users.session_users) { + asprintf(&p, "%s", suser); + } else { + asprintf(&p, "%s %s", smb->users.session_users, suser); + } + SAFE_FREE(smb->users.session_users); + smb->users.session_users = p; + } + + free(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))) { + 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))) { + 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 const char *validate_group(struct server_context *smb, const 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(smb, 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) { + const char *name = member; + if (user_ok(name,snum, NULL, 0) && + password_ok(smb,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(struct server_context *smb, + int snum, const char *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(smb->users.session_users); + if (!user_list) + return(False); + + for (auser=strtok(user_list,LIST_SEP); !ok && auser; + auser = strtok(NULL,LIST_SEP)) { + const char *user2 = auser; + + if (!user_ok(user2,snum, NULL, 0)) + continue; + + if (password_ok(smb, user2,password)) { + ok = True; + DEBUG(3,("authorise_login: ACCEPTED: session list username (%s) \ +and given password ok\n", user2)); + } + } + + SAFE_FREE(user_list); + } + + /* check the user= fields and the given password */ + if (!ok && lp_username(snum)) { + const char *auser; + pstring user_list; + StrnCpy(user_list,lp_username(snum),sizeof(pstring)); + + 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(smb, auser+1,password,snum); + if (auser) { + ok = True; + DEBUG(3,("authorise_login: ACCEPTED: group username \ +and given password ok (%s)\n", auser)); + } + } else { + const char *user2 = auser; + if (user_ok(user2,snum, NULL, 0) && password_ok(smb, user2,password)) { + ok = True; + DEBUG(3,("authorise_login: ACCEPTED: user list username \ +and given password ok (%s)\n", user2)); + } + } + } + } + + /* check for a normal guest connection */ + if (!ok && GUEST_OK(snum)) { + const char *guestname = lp_guestaccount(); + if (Get_Pwnam(guestname)) { + ok = True; + DEBUG(3,("authorise_login: ACCEPTED: guest account and guest ok (%s)\n", guestname)); + } 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/source4/smbd/process.c b/source4/smbd/process.c new file mode 100644 index 0000000000..a4e67c7ff1 --- /dev/null +++ b/source4/smbd/process.c @@ -0,0 +1,833 @@ +/* + Unix SMB/CIFS implementation. + process incoming packets - main loop + Copyright (C) Andrew Tridgell 1992-2003 + Copyright (C) James J Myers 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +SIG_ATOMIC_T reload_after_sighup = 0; +SIG_ATOMIC_T got_sig_term = 0; + +/* + send an oplock break request to a client +*/ +BOOL req_send_oplock_break(struct tcon_context *conn, uint16 fnum, uint8 level) +{ + struct request_context *req; + + req = init_smb_request(conn->smb); + + req_setup_reply(req, 8, 0); + + SCVAL(req->out.hdr,HDR_COM,SMBlockingX); + SSVAL(req->out.hdr,HDR_TID,conn->cnum); + SSVAL(req->out.hdr,HDR_PID,0xFFFF); + SSVAL(req->out.hdr,HDR_UID,0); + SSVAL(req->out.hdr,HDR_MID,0xFFFF); + SCVAL(req->out.hdr,HDR_FLG,0); + SSVAL(req->out.hdr,HDR_FLG2,0); + + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + SSVAL(req->out.vwv, VWV(2), fnum); + SSVAL(req->out.vwv, VWV(3), level); + SIVAL(req->out.vwv, VWV(4), 0); + SSVAL(req->out.vwv, VWV(6), 0); + SSVAL(req->out.vwv, VWV(7), 0); + + req_send_reply(req); + return True; +} + +/**************************************************************************** +receive a SMB request from the wire, forming a request_context from the result +****************************************************************************/ +static struct request_context *receive_smb_request(struct server_context *smb) +{ + ssize_t len, len2; + char header[4]; + struct request_context *req; + + len = read_data(smb->socket.fd, header, 4); + if (len != 4) { + return NULL; + } + + len = smb_len(header); + + req = init_smb_request(smb); + + GetTimeOfDay(&req->request_time); + req->chained_fnum = -1; + + /* allocate the incoming buffer at the right size */ + req->in.buffer = talloc(req->mem_ctx, len + NBT_HDR_SIZE); + + /* fill in the already received header */ + memcpy(req->in.buffer, header, 4); + + len2 = read_data(smb->socket.fd, req->in.buffer + NBT_HDR_SIZE, len); + if (len2 != len) { + return NULL; + } + + /* fill in the rest of the req->in structure */ + req->in.size = len + NBT_HDR_SIZE; + req->in.allocated = req->in.size; + req->in.hdr = req->in.buffer + NBT_HDR_SIZE; + req->in.vwv = req->in.hdr + HDR_VWV; + req->in.wct = CVAL(req->in.hdr, HDR_WCT); + if (req->in.vwv + VWV(req->in.wct) <= req->in.buffer + req->in.size) { + req->in.data = req->in.vwv + VWV(req->in.wct) + 2; + req->in.data_size = SVAL(req->in.vwv, VWV(req->in.wct)); + + /* the bcc length is only 16 bits, but some packets + (such as SMBwriteX) can be much larger than 64k. We + detect this by looking for a large non-chained NBT + packet (at least 64k bigger than what is + specified). If it is detected then the NBT size is + used instead of the bcc size */ + if (req->in.data_size + 0x10000 <= + req->in.size - PTR_DIFF(req->in.data, req->in.buffer) && + (req->in.wct < 1 || SVAL(req->in.vwv, VWV(0)) == SMB_CHAIN_NONE)) { + /* its an oversized packet! fun for all the family */ + req->in.data_size = req->in.size - PTR_DIFF(req->in.data,req->in.buffer); + } + } + + return req; +} + +/* + setup the user_ctx element of a request +*/ +static void setup_user_context(struct request_context *req) +{ + struct user_context *ctx; + + ctx = talloc(req->mem_ctx, sizeof(*ctx)); + ctx->vuid = SVAL(req->in.hdr, HDR_UID); + ctx->vuser = get_valid_user_struct(req->smb, ctx->vuid); + + req->user_ctx = ctx; +} + + +/* +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 USE_MUTEX (1<<7) + +/* + define a list of possible SMB messages and their corresponding + functions. Any message that has a NULL function is unimplemented - + please feel free to contribute implementations! +*/ +static const struct smb_message_struct +{ + const char *name; + void (*fn)(struct request_context *); + 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 }, +/* 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 }, +/* 0x07 */ { "SMBmv",reply_mv,AS_USER | NEED_WRITE }, +/* 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 }, +/* 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 }, +/* 0x2a */ { "SMBmove",NULL,AS_USER | NEED_WRITE }, +/* 0x2b */ { "SMBecho",reply_echo,0}, +/* 0x2c */ { "SMBwriteclose",reply_writeclose,AS_USER}, +/* 0x2d */ { "SMBopenX",reply_open_and_X,AS_USER | CAN_IPC }, +/* 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,USE_MUTEX}, +/* 0x71 */ { "SMBtdis",reply_tdis,0}, +/* 0x72 */ { "SMBnegprot",reply_negprot,USE_MUTEX}, +/* 0x73 */ { "SMBsesssetupX",reply_sesssetup,USE_MUTEX}, +/* 0x74 */ { "SMBulogoffX", reply_ulogoffX, 0}, /* ulogoff doesn't give a valid TID */ +/* 0x75 */ { "SMBtconX",reply_tcon_and_X,USE_MUTEX}, +/* 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 }, +/* 0xa1 */ { "SMBnttranss", reply_nttranss, AS_USER | CAN_IPC }, +/* 0xa2 */ { "SMBntcreateX", reply_ntcreate_and_X, AS_USER | CAN_IPC }, +/* 0xa3 */ { NULL, NULL, 0 }, +/* 0xa4 */ { "SMBntcancel", reply_ntcancel, 0 }, +/* 0xa5 */ { NULL, NULL, 0 }, +/* 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 }, +/* 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 } +}; + +/**************************************************************************** +return a string containing the function name of a SMB command +****************************************************************************/ +static const char *smb_fn_name(uint8 type) +{ + const char *unknown_name = "SMBunknown"; + + if (smb_messages[type].name == NULL) + return unknown_name; + + return smb_messages[type].name; +} + + +/**************************************************************************** + Do a switch on the message type and call the specific reply function for this +message. Unlike earlier versions of Samba the reply functions are responsible +for sending the reply themselves, rather than returning a size to this function +The reply functions may also choose to delay the processing by pushing the message +onto the message queue +****************************************************************************/ +static void switch_message(int type, struct request_context *req) +{ + int flags; + uint16 session_tag; + struct server_context *smb = req->smb; + + type &= 0xff; + + errno = 0; + + if (smb_messages[type].fn == NULL) { + DEBUG(0,("Unknown message type %d!\n",type)); + reply_unknown(req); + return; + } + + flags = smb_messages[type].flags; + + /* In share mode security we must ignore the vuid. */ + session_tag = (lp_security() == SEC_SHARE) ? + UID_FIELD_INVALID : + SVAL(req->in.hdr,HDR_UID); + + req->conn = conn_find(req->smb, SVAL(req->in.hdr,HDR_TID)); + + /* setup the user context for this request */ + setup_user_context(req); + + /* Ensure this value is replaced in the incoming packet. */ + SSVAL(req->in.hdr,HDR_UID,session_tag); + + if (req->user_ctx) { + req->user_ctx->vuid = session_tag; + } + DEBUG(3,("switch message %s (task_id %d)\n",smb_fn_name(type), smb->model_ops->get_id(req))); + + /* 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) && !req->conn) { + req_reply_error(req, NT_STATUS_NETWORK_NAME_DELETED); + return; + } + + /* does this protocol need to be run as the connected user? */ +#if HACK_REWRITE + if ((flags & AS_USER) && !change_to_user(req->conn,session_tag)) { + if (!(flags & AS_GUEST)) { + req_reply_error(req, NT_STATUS_ACCESS_DENIED); + return; + } + + /* we'll run it as guest */ + flags &= ~AS_USER; + } +#endif + + /* 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(req->conn)) { + req_reply_error(req, NT_STATUS_ACCESS_DENIED); + return; + } + + /* ipc services are limited */ + if (req->conn && req->conn->type == NTVFS_IPC && (flags & AS_USER) && !(flags & CAN_IPC)) { + req_reply_error(req, NT_STATUS_ACCESS_DENIED); + return; + } + + /* load service specific parameters */ + if (req->conn && !set_current_service(req->conn,(flags & AS_USER)?True:False)) { + req_reply_error(req, NT_STATUS_ACCESS_DENIED); + return; + } + + /* does this protocol need to be run as guest? */ +#if HACK_REWRITE + if ((flags & AS_GUEST) && + !change_to_guest()) { + req_reply_error(req, NT_STATUS_ACCESS_DENIED); + return; + } +#endif + /* THREAD TESTING: use mutex to serialize calls to critical functions with global state */ + if (flags & USE_MUTEX) { + MUTEX_LOCK_BY_ID(MUTEX_SMBD); + } + smb_messages[type].fn(req); + if (flags & USE_MUTEX) { + MUTEX_UNLOCK_BY_ID(MUTEX_SMBD); + } +} + + +/**************************************************************************** + Construct a reply to the incoming packet. +****************************************************************************/ +static void construct_reply(struct request_context *req) +{ + uint8 type = CVAL(req->in.hdr,HDR_COM); + + /* see if its a special NBT packet */ + if (CVAL(req->in.buffer,0) != 0) { + reply_special(req); + return; + } + + + /* Make sure this is an SMB packet */ + if (memcmp(req->in.hdr,"\377SMB",4) != 0) { + DEBUG(2,("Non-SMB packet of length %d. Terminating connection\n", + req->in.size)); + exit_server(req->smb, "Non-SMB packet"); + return; + } + + if (NBT_HDR_SIZE + MIN_SMB_SIZE + 2*req->in.wct > req->in.size) { + DEBUG(2,("Invalid SMB word count %d\n", req->in.wct)); + exit_server(req->smb, "Invalid SMB packet"); + return; + } + + if (NBT_HDR_SIZE + MIN_SMB_SIZE + 2*req->in.wct + req->in.data_size > req->in.size) { + DEBUG(2,("Invalid SMB buffer length count %d\n", req->in.data_size)); + exit_server(req->smb, "Invalid SMB packet"); + return; + } + + + req->smbpid = SVAL(req->in.hdr,HDR_PID); + req->flags = CVAL(req->in.hdr, HDR_FLG); + req->flags2 = SVAL(req->in.hdr, HDR_FLG2); + + switch_message(type, req); +} + + +/* + we call this when first first part of a possibly chained request has been completed + and we need to call the 2nd part, if any +*/ +void chain_reply(struct request_context *req) +{ + uint16 chain_cmd, chain_offset; + char *vwv, *data; + uint16 wct; + uint16 data_size; + + if (req->in.wct < 2 || req->out.wct < 2) { + req_reply_dos_error(req, ERRSRV, ERRerror); + return; + } + + chain_cmd = CVAL(req->in.vwv, VWV(0)); + chain_offset = SVAL(req->in.vwv, VWV(1)); + + if (chain_cmd == SMB_CHAIN_NONE) { + /* end of chain */ + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + req_send_reply(req); + return; + } + + if (chain_offset + req->in.hdr >= req->in.buffer + req->in.size) { + goto error; + } + + wct = CVAL(req->in.hdr, chain_offset); + vwv = req->in.hdr + chain_offset + 1; + + if (vwv + VWV(wct) + 2 > req->in.buffer + req->in.size) { + goto error; + } + + data_size = SVAL(vwv, VWV(wct)); + data = vwv + VWV(wct) + 2; + + if (data + data_size > req->in.buffer + req->in.size) { + goto error; + } + + /* all seems legit */ + req->in.vwv = vwv; + req->in.wct = wct; + req->in.data = data; + req->in.data_size = data_size; + req->in.ptr = data; + + req->chain_count++; + + SSVAL(req->out.vwv, VWV(0), chain_cmd); + SSVAL(req->out.vwv, VWV(1), req->out.size - NBT_HDR_SIZE); + + /* the current request in the chain might have used an async reply, + but that doesn't mean the next element needs to */ + ZERO_STRUCT(req->async); + req->control_flags &= ~REQ_CONTROL_ASYNC; + + switch_message(chain_cmd, req); + return; + +error: + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + req_reply_dos_error(req, ERRSRV, ERRerror); +} + + +/* + close the socket and shutdown a server_context +*/ +void server_terminate(struct server_context *smb) +{ + close(smb->socket.fd); + event_remove_fd_all(smb->events, smb->socket.fd); + + conn_close_all(smb); + + talloc_destroy(smb->mem_ctx); +} + + +/* + called when a SMB socket becomes readable +*/ +static void smbd_read_handler(struct event_context *ev, struct fd_event *fde, + time_t t, uint16 flags) +{ + struct request_context *req; + struct server_context *smb = fde->private; + + req = receive_smb_request(smb); + if (!req) { + smb->model_ops->terminate_connection(smb, "receive error"); + return; + } + + construct_reply(req); + + /* free up temporary memory */ + lp_talloc_free(); +} + + +/* + process a message from an SMB socket while still processing a + previous message this is used by backends who need to ensure that + new messages from clients are still processed while they are + performing long operations +*/ +void smbd_process_async(struct server_context *smb) +{ + struct request_context *req; + + req = receive_smb_request(smb); + if (!req) { + smb->model_ops->terminate_connection(smb, "receive error"); + return; + } + + construct_reply(req); +} + + +/* + initialise a server_context from a open socket and register a event handler + for reading from that socket +*/ +void init_smbsession(struct event_context *ev, struct model_ops *model_ops, int fd) +{ + struct server_context *smb; + TALLOC_CTX *mem_ctx; + struct fd_event fde; + + set_socket_options(fd,"SO_KEEPALIVE"); + set_socket_options(fd, lp_socket_options()); + + mem_ctx = talloc_init("server_context"); + + smb = (struct server_context *)talloc(mem_ctx, sizeof(*smb)); + if (!smb) return; + + ZERO_STRUCTP(smb); + + smb->mem_ctx = mem_ctx; + smb->socket.fd = fd; + smb->pid = getpid(); + + sub_set_context(&smb->substitute); + + /* set an initial client name based on its IP address. This will be replaced with + the netbios name later if it gives us one */ + sub_set_remote_machine(strdup(get_socket_addr(smb->mem_ctx, fd))); + smb->socket.client_addr = talloc_strdup(smb->mem_ctx, get_socket_addr(smb->mem_ctx, fd)); + + /* now initialise a few default values associated with this smb socket */ + smb->negotiate.max_send = 0xFFFF; + + /* this is the size that w2k uses, and it appears to be important for + good performance */ + smb->negotiate.max_recv = 4356; + + smb->users.next_vuid = VUID_OFFSET; + + smb->events = ev; + smb->model_ops = model_ops; + + conn_init(smb); + + /* setup a event handler for this socket. We are initially + only interested in reading from the socket */ + fde.fd = fd; + fde.handler = smbd_read_handler; + fde.private = smb; + fde.flags = EVENT_FD_READ; + + event_add_fd(ev, &fde); +} + + +/* + * initialize an smb process + */ +void smbd_process_init(void) +{ + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_init("smbd_process_init talloc"); + if (!mem_ctx) { + DEBUG(0,("smbd_process_init: ERROR: No memory\n")); + exit(1); + } + namecache_enable(); + + if (!locking_init(0)) + exit(1); + + if (!share_info_db_init()) + exit(1); + + if (!init_registry()) + exit(1); + + if(!initialize_password_db(False)) + exit(1); + + /* possibly reload the services file. */ + reload_services(NULL, True); + + if(!get_global_sam_sid()) { + DEBUG(0,("ERROR: Samba cannot create a SAM SID.\n")); + exit(1); + } + + 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); + + /* Setup privileges database */ + if (!privilege_init()) + exit(1); + + /* Setup the NTVFS subsystem */ + if (!ntvfs_init()) + exit(1); + + /* re-initialise the timezone */ + TimeInit(); + + talloc_destroy(mem_ctx); +} + diff --git a/source4/smbd/process_model.c b/source4/smbd/process_model.c new file mode 100644 index 0000000000..06127d1364 --- /dev/null +++ b/source4/smbd/process_model.c @@ -0,0 +1,85 @@ +/* + Unix SMB/CIFS implementation. + process model manager - main loop + Copyright (C) Andrew Tridgell 1992-2003 + Copyright (C) James J Myers 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 list of currently registered process models */ +static struct { + const char *name; + struct model_ops *ops; +} *models = NULL; +static int num_models; + +/* + register a process model. + + The 'name' can be later used by other backends to find the operations + structure for this backend. +*/ +BOOL register_process_model(const char *name, struct model_ops *ops) +{ + if (process_model_byname(name) != NULL) { + /* its already registered! */ + DEBUG(2,("process_model '%s' already registered\n", + name)); + return False; + } + + models = Realloc(models, sizeof(models[0]) * (num_models+1)); + if (!models) { + smb_panic("out of memory in register_process_model"); + } + + models[num_models].name = smb_xstrdup(name); + models[num_models].ops = smb_xmemdup(ops, sizeof(*ops)); + + num_models++; + + return True; +} + +/* + return the operations structure for a named backend of the specified type +*/ +struct model_ops *process_model_byname(const char *name) +{ + int i; + + for (i=0;i + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +/* + called when the process model is selected +*/ +static void model_startup(void) +{ + smbd_process_init(); +} + +/* + called when a listening socket becomes readable +*/ +static void accept_connection(struct event_context *ev, struct fd_event *fde, time_t t, uint16 flags) +{ + int accepted_fd; + struct sockaddr addr; + socklen_t in_addrlen = sizeof(addr); + struct model_ops *model_ops = fde->private; + + /* accept an incoming connection. */ + accepted_fd = accept(fde->fd,&addr,&in_addrlen); + if (accepted_fd == -1) { + DEBUG(0,("accept_connection_single: accept: %s\n", + strerror(errno))); + return; + } + + /* create a smb server context and add it to out event + handling */ + init_smbsession(ev, model_ops, accepted_fd); + + /* return to event handling */ +} + +/* called when a SMB connection goes down */ +static void terminate_connection(struct server_context *server, const char *reason) +{ + server_terminate(server); +} + +static int get_id(struct request_context *req) +{ + return (int)req->smb->pid; +} + +/* + initialise the single process model, registering ourselves with the model subsystem + */ +void process_model_single_init(void) +{ + struct model_ops ops; + + ZERO_STRUCT(ops); + + /* fill in all the operations */ + ops.model_startup = model_startup; + ops.accept_connection = accept_connection; + ops.terminate_connection = terminate_connection; + ops.exit_server = NULL; + ops.get_id = get_id; + + /* register ourselves with the process model subsystem. We register under the name 'single'. */ + register_process_model("single", &ops); +} diff --git a/source4/smbd/process_standard.c b/source4/smbd/process_standard.c new file mode 100644 index 0000000000..f12784bc79 --- /dev/null +++ b/source4/smbd/process_standard.c @@ -0,0 +1,110 @@ +/* + Unix SMB/CIFS implementation. + process model: standard (1 process per client connection) + Copyright (C) Andrew Tridgell 1992-2003 + Copyright (C) James J Myers 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +/* + called when the process model is selected +*/ +static void model_startup(void) +{ +} + +/* + called when a listening socket becomes readable +*/ +static void accept_connection(struct event_context *ev, struct fd_event *fde, time_t t, uint16 flags) +{ + int accepted_fd; + struct sockaddr addr; + socklen_t in_addrlen = sizeof(addr); + pid_t pid; + struct model_ops *model_ops = fde->private; + + accepted_fd = accept(fde->fd,&addr,&in_addrlen); + if (accepted_fd == -1) { + DEBUG(0,("accept_connection_standard: accept: %s\n", + strerror(errno))); + return; + } + + pid = fork(); + + if (pid != 0) { + /* parent or error code ... */ + + close(accepted_fd); + /* go back to the event loop */ + return; + } + + /* Child code ... */ + + /* close all the listening sockets */ + event_remove_fd_all_handler(ev, accept_connection); + + /* tdb needs special fork handling */ + if (tdb_reopen_all() == -1) { + DEBUG(0,("accept_connection_standard: tdb_reopen_all failed.\n")); + } + + /* Load DSO's */ + init_modules(); + + /* initialize new process */ + smbd_process_init(); + + init_smbsession(ev, model_ops, accepted_fd); + + /* return to the event loop */ +} + +/* called when a SMB connection goes down */ +static void terminate_connection(struct server_context *server, const char *reason) +{ + server_terminate(server); + /* terminate this process */ + exit(0); +} + +static int get_id(struct request_context *req) +{ + return (int)req->smb->pid; +} + +/* + initialise the standard process model, registering ourselves with the model subsystem + */ +void process_model_standard_init(void) +{ + struct model_ops ops; + + ZERO_STRUCT(ops); + + /* fill in all the operations */ + ops.model_startup = model_startup; + ops.accept_connection = accept_connection; + ops.terminate_connection = terminate_connection; + ops.get_id = get_id; + + /* register ourselves with the process model subsystem. We register under the name 'standard'. */ + register_process_model("standard", &ops); +} diff --git a/source4/smbd/process_thread.c b/source4/smbd/process_thread.c new file mode 100644 index 0000000000..cd8865f1e4 --- /dev/null +++ b/source4/smbd/process_thread.c @@ -0,0 +1,410 @@ +/* + Unix SMB/CIFS implementation. + thread model: standard (1 thread per client connection) + Copyright (C) Andrew Tridgell 2003 + Copyright (C) James J Myers 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 "pthread.h" +#include "execinfo.h" + +static void *connection_thread(void *thread_parm) +{ + struct event_context *ev = thread_parm; + /* wait for action */ + event_loop_wait(ev); + +#if 0 + pthread_cleanup_pop(1); /* will invoke terminate_mt_connection() */ +#endif + return NULL; +} + +static int get_id(struct request_context *req) +{ + return (int)pthread_self(); +} + +/* + called when a listening socket becomes readable +*/ +static void accept_connection(struct event_context *ev, struct fd_event *fde, time_t t, uint16 flags) +{ + int accepted_fd, rc; + struct sockaddr addr; + socklen_t in_addrlen = sizeof(addr); + pthread_t thread_id; + pthread_attr_t thread_attr; + struct model_ops *model_ops = fde->private; + + /* accept an incoming connection */ + accepted_fd = accept(fde->fd,&addr,&in_addrlen); + + if (accepted_fd == -1) { + DEBUG(0,("accept_connection_thread: accept: %s\n", + strerror(errno))); + return; + } + + /* create new detached thread for this connection. The new + thread gets a new event_context with a single fd_event for + receiving from the new socket. We set that thread running + with the main event loop, then return. When we return the + main event_context is continued. + */ + ev = event_context_init(); + MUTEX_LOCK_BY_ID(MUTEX_SMBD); + init_smbsession(ev, model_ops, accepted_fd); + MUTEX_UNLOCK_BY_ID(MUTEX_SMBD); + + pthread_attr_init(&thread_attr); + pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED); + rc = pthread_create(&thread_id, &thread_attr, &connection_thread, ev); + pthread_attr_destroy(&thread_attr); + if (rc == 0) { + DEBUG(4,("accept_connection_thread: created thread_id=%lu for fd=%d\n", + (unsigned long int)thread_id, accepted_fd)); + } else { + DEBUG(0,("accept_connection_thread: thread create failed for fd=%d, rc=%d\n", accepted_fd, rc)); + } +} + +/* called when a SMB connection goes down */ +static void terminate_connection(struct server_context *server, const char *reason) +{ + server_terminate(server); + + /* terminate this thread */ + pthread_exit(NULL); /* thread cleanup routine will do actual cleanup */ +} + +/* + mutex init function for thread model +*/ +static int thread_mutex_init(mutex_t *mutex, const char *name) +{ + pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; + mutex->mutex = memdup(&m, sizeof(m)); + if (! mutex->mutex) { + errno = ENOMEM; + return -1; + } + return pthread_mutex_init((pthread_mutex_t *)mutex->mutex, NULL); +} + +/* + mutex destroy function for thread model +*/ +static int thread_mutex_destroy(mutex_t *mutex, const char *name) +{ + return pthread_mutex_destroy((pthread_mutex_t *)mutex->mutex); +} + +static void mutex_start_timer(struct timeval *tp1) +{ + gettimeofday(tp1,NULL); +} + +static double mutex_end_timer(struct timeval tp1) +{ + struct timeval tp2; + gettimeofday(&tp2,NULL); + return((tp2.tv_sec - tp1.tv_sec) + + (tp2.tv_usec - tp1.tv_usec)*1.0e-6); +} + +/* + mutex lock function for thread model +*/ +static int thread_mutex_lock(mutex_t *mutexP, const char *name) +{ + pthread_mutex_t *mutex = (pthread_mutex_t *)mutexP->mutex; + int rc; + double t; + struct timeval tp1; + /* Test below is ONLY for debugging */ + if ((rc = pthread_mutex_trylock(mutex))) { + if (rc == EBUSY) { + mutex_start_timer(&tp1); + printf("mutex lock: thread %d, lock %s not available\n", + (uint32)pthread_self(), name); + print_suspicious_usage("mutex_lock", name); + pthread_mutex_lock(mutex); + t = mutex_end_timer(tp1); + printf("mutex lock: thread %d, lock %s now available, waited %g seconds\n", + (uint32)pthread_self(), name, t); + return 0; + } + printf("mutex lock: thread %d, lock %s failed rc=%d\n", + (uint32)pthread_self(), name, rc); + SMB_ASSERT(errno == 0); /* force error */ + } + return 0; +} + +/* + mutex unlock for thread model +*/ +static int thread_mutex_unlock(mutex_t *mutex, const char *name) +{ + return pthread_mutex_unlock((pthread_mutex_t *)mutex->mutex); +} + +/***************************************************************** + Read/write lock routines. +*****************************************************************/ +/* + rwlock init function for thread model +*/ +static int thread_rwlock_init(rwlock_t *rwlock, const char *name) +{ + pthread_rwlock_t m = PTHREAD_RWLOCK_INITIALIZER; + rwlock->rwlock = memdup(&m, sizeof(m)); + if (! rwlock->rwlock) { + errno = ENOMEM; + return -1; + } + return pthread_rwlock_init((pthread_rwlock_t *)rwlock->rwlock, NULL); +} + +/* + rwlock destroy function for thread model +*/ +static int thread_rwlock_destroy(rwlock_t *rwlock, const char *name) +{ + return pthread_rwlock_destroy((pthread_rwlock_t *)rwlock->rwlock); +} + +/* + rwlock lock for read function for thread model +*/ +static int thread_rwlock_lock_read(rwlock_t *rwlockP, const char *name) +{ + pthread_rwlock_t *rwlock = (pthread_rwlock_t *)rwlockP->rwlock; + int rc; + double t; + struct timeval tp1; + /* Test below is ONLY for debugging */ + if ((rc = pthread_rwlock_tryrdlock(rwlock))) { + if (rc == EBUSY) { + mutex_start_timer(&tp1); + printf("rwlock lock_read: thread %d, lock %s not available\n", + (uint32)pthread_self(), name); + print_suspicious_usage("rwlock_lock_read", name); + pthread_rwlock_rdlock(rwlock); + t = mutex_end_timer(tp1); + printf("rwlock lock_read: thread %d, lock %s now available, waited %g seconds\n", + (uint32)pthread_self(), name, t); + return 0; + } + printf("rwlock lock_read: thread %d, lock %s failed rc=%d\n", + (uint32)pthread_self(), name, rc); + SMB_ASSERT(errno == 0); /* force error */ + } + return 0; +} + +/* + rwlock lock for write function for thread model +*/ +static int thread_rwlock_lock_write(rwlock_t *rwlockP, const char *name) +{ + pthread_rwlock_t *rwlock = (pthread_rwlock_t *)rwlockP->rwlock; + int rc; + double t; + struct timeval tp1; + /* Test below is ONLY for debugging */ + if ((rc = pthread_rwlock_trywrlock(rwlock))) { + if (rc == EBUSY) { + mutex_start_timer(&tp1); + printf("rwlock lock_write: thread %d, lock %s not available\n", + (uint32)pthread_self(), name); + print_suspicious_usage("rwlock_lock_write", name); + pthread_rwlock_wrlock(rwlock); + t = mutex_end_timer(tp1); + printf("rwlock lock_write: thread %d, lock %s now available, waited %g seconds\n", + (uint32)pthread_self(), name, t); + return 0; + } + printf("rwlock lock_write: thread %d, lock %s failed rc=%d\n", + (uint32)pthread_self(), name, rc); + SMB_ASSERT(errno == 0); /* force error */ + } + return 0; +} + + +/* + rwlock unlock for thread model +*/ +static int thread_rwlock_unlock(rwlock_t *rwlock, const char *name) +{ + return pthread_rwlock_unlock((pthread_rwlock_t *)rwlock->rwlock); +} + +/***************************************************************** + Log suspicious usage (primarily for possible thread-unsafe behavior. +*****************************************************************/ +static void thread_log_suspicious_usage(const char* from, const char* info) +{ + void *addresses[10]; + int num_addresses, i; + char **bt_symbols; + + DEBUG(1,("log_suspicious_usage: from %s info='%s'\n", from, info)); + num_addresses = backtrace(addresses, 8); + bt_symbols = backtrace_symbols(addresses, num_addresses); + for (i=0; iin.wct != (wcount)) { \ + DEBUG(1,("Unexpected WCT %d at %s(%d) - expected %d\n", \ + (req)->in.wct, __FILE__, __LINE__, wcount)); \ + req_reply_dos_error(req, ERRSRV, ERRerror); \ + return; \ + }} while (0) + +/* check req->async.status and if not OK then send an error reply */ +#define CHECK_ASYNC_STATUS do { \ + if (!NT_STATUS_IS_OK(req->async.status)) { \ + req_reply_error(req, req->async.status); \ + return; \ + }} while (0) + +/* useful wrapper for talloc with NO_MEMORY reply */ +#define REQ_TALLOC(ptr, size) do { \ + ptr = talloc(req->mem_ctx, size); \ + if (!ptr) { \ + req_reply_error(req, NT_STATUS_NO_MEMORY); \ + return; \ + }} while (0) + +/* + check if the backend wants to handle the request asynchronously. + if it wants it handled synchronously then call the send function + immediately +*/ +#define REQ_ASYNC_TAIL do { \ + if (!(req->control_flags & REQ_CONTROL_ASYNC)) { \ + req->async.send_fn(req); \ + }} while (0) + +/* zero out some reserved fields in a reply */ +#define REQ_VWV_RESERVED(start, count) memset(req->out.vwv + VWV(start), 0, (count)*2) + +/* + put a NTTIME into a packet +*/ +void push_nttime(void *base, uint16 offset, NTTIME *t) +{ + SIVAL(base, offset, t->low); + SIVAL(base, offset+4, t->high); +} + +/* + pull a NTTIME from a packet +*/ +NTTIME pull_nttime(void *base, uint16 offset) +{ + NTTIME ret; + ret.low = IVAL(base, offset); + ret.high = IVAL(base, offset+4); + return ret; +} + + +/**************************************************************************** + Reply to a simple request (async send) +****************************************************************************/ +static void reply_simple_send(struct request_context *req) +{ + CHECK_ASYNC_STATUS; + + req_setup_reply(req, 0, 0); + req_send_reply(req); +} + + +/**************************************************************************** + Reply to a tcon. +****************************************************************************/ +void reply_tcon(struct request_context *req) +{ + union smb_tcon con; + NTSTATUS status; + char *p; + + /* parse request */ + REQ_CHECK_WCT(req, 0); + + con.tcon.level = RAW_TCON_TCON; + + p = req->in.data; + p += req_pull_ascii4(req, &con.tcon.in.service, p, STR_TERMINATE); + p += req_pull_ascii4(req, &con.tcon.in.password, p, STR_TERMINATE); + p += req_pull_ascii4(req, &con.tcon.in.dev, p, STR_TERMINATE); + + if (!con.tcon.in.service || !con.tcon.in.password || !con.tcon.in.dev) { + req_reply_error(req, NT_STATUS_INVALID_PARAMETER); + return; + } + + /* call backend */ + status = tcon_backend(req, &con); + + if (!NT_STATUS_IS_OK(status)) { + req_reply_error(req, status); + return; + } + + /* construct reply */ + req_setup_reply(req, 2, 0); + + SSVAL(req->out.vwv, VWV(0), con.tcon.out.max_xmit); + SSVAL(req->out.vwv, VWV(1), con.tcon.out.cnum); + SSVAL(req->out.hdr, HDR_TID, req->conn->cnum); + + req_send_reply(req); +} + + +/**************************************************************************** + Reply to a tcon and X. +****************************************************************************/ +void reply_tcon_and_X(struct request_context *req) +{ + NTSTATUS status; + union smb_tcon con; + char *p; + uint16 passlen; + + con.tconx.level = RAW_TCON_TCONX; + + /* parse request */ + REQ_CHECK_WCT(req, 4); + + con.tconx.in.flags = SVAL(req->in.vwv, VWV(2)); + passlen = SVAL(req->in.vwv, VWV(3)); + + p = req->in.data; + + if (!req_pull_blob(req, p, passlen, &con.tconx.in.password)) { + req_reply_error(req, NT_STATUS_ILL_FORMED_PASSWORD); + return; + } + p += passlen; + + p += req_pull_string(req, &con.tconx.in.path, p, -1, STR_TERMINATE); + p += req_pull_string(req, &con.tconx.in.device, p, -1, STR_ASCII); + + if (!con.tconx.in.path || !con.tconx.in.device) { + req_reply_error(req, NT_STATUS_BAD_DEVICE_TYPE); + return; + } + + /* call backend */ + status = tcon_backend(req, &con); + + if (!NT_STATUS_IS_OK(status)) { + req_reply_error(req, status); + return; + } + + /* construct reply - two varients */ + if (req->smb->negotiate.protocol < PROTOCOL_NT1) { + req_setup_reply(req, 2, 0); + + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + + req_push_str(req, NULL, con.tconx.out.dev_type, -1, STR_TERMINATE|STR_ASCII); + } else { + req_setup_reply(req, 3, 0); + + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + SSVAL(req->out.vwv, VWV(2), con.tconx.out.options); + + req_push_str(req, NULL, con.tconx.out.dev_type, -1, STR_TERMINATE|STR_ASCII); + req_push_str(req, NULL, con.tconx.out.fs_type, -1, STR_TERMINATE); + } + + /* set the incoming and outgoing tid to the just created one */ + SSVAL(req->in.hdr, HDR_TID, con.tconx.out.cnum); + SSVAL(req->out.hdr,HDR_TID, con.tconx.out.cnum); + + chain_reply(req); +} + + +/**************************************************************************** + Reply to an unknown request +****************************************************************************/ +void reply_unknown(struct request_context *req) +{ + int type; + + type = CVAL(req->in.hdr, HDR_COM); + + DEBUG(0,("unknown command type %d (0x%X)\n", type, type)); + + req_reply_dos_error(req, ERRSRV, ERRunknownsmb); +} + + +/**************************************************************************** + Reply to an ioctl (async reply) +****************************************************************************/ +static void reply_ioctl_send(struct request_context *req) +{ + struct smb_ioctl *io = req->async.private; + + CHECK_ASYNC_STATUS; + + /* the +1 is for nicer alignment */ + req_setup_reply(req, 8, io->out.blob.length+1); + SSVAL(req->out.vwv, VWV(1), io->out.blob.length); + SSVAL(req->out.vwv, VWV(5), io->out.blob.length); + SSVAL(req->out.vwv, VWV(6), PTR_DIFF(req->out.data, req->out.hdr) + 1); + + memcpy(req->out.data+1, io->out.blob.data, io->out.blob.length); + + req_send_reply(req); +} + +/**************************************************************************** + Reply to an ioctl. +****************************************************************************/ +void reply_ioctl(struct request_context *req) +{ + struct smb_ioctl *io; + + /* parse requst */ + REQ_CHECK_WCT(req, 3); + REQ_TALLOC(io, sizeof(*io)); + + io->in.fnum = req_fnum(req, req->in.vwv, VWV(0)); + io->in.request = IVAL(req->in.vwv, VWV(1)); + + req->async.send_fn = reply_ioctl_send; + req->async.private = io; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->ioctl(req, io); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a chkpth. +****************************************************************************/ +void reply_chkpth(struct request_context *req) +{ + struct smb_chkpath *io; + + REQ_TALLOC(io, sizeof(*io)); + + req_pull_ascii4(req, &io->in.path, req->in.data, STR_TERMINATE); + + req->async.send_fn = reply_simple_send; + + req->async.status = req->conn->ntvfs_ops->chkpath(req, io); + + REQ_ASYNC_TAIL; +} + +/**************************************************************************** + Reply to a getatr (async reply) +****************************************************************************/ +static void reply_getatr_send(struct request_context *req) +{ + union smb_fileinfo *st = req->async.private; + + CHECK_ASYNC_STATUS; + + /* construct reply */ + req_setup_reply(req, 10, 0); + + SSVAL(req->out.vwv, VWV(0), st->getattr.out.attrib); + put_dos_date3(req->out.vwv, VWV(1), st->getattr.out.write_time); + SIVAL(req->out.vwv, VWV(3), st->getattr.out.size); + + REQ_VWV_RESERVED(5, 5); + + req_send_reply(req); +} + + +/**************************************************************************** + Reply to a getatr. +****************************************************************************/ +void reply_getatr(struct request_context *req) +{ + union smb_fileinfo *st; + + REQ_TALLOC(st, sizeof(*st)); + + st->getattr.level = RAW_FILEINFO_GETATTR; + + /* parse request */ + req_pull_ascii4(req, &st->getattr.in.fname, req->in.data, STR_TERMINATE); + if (!st->getattr.in.fname) { + req_reply_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND); + return; + } + + req->async.send_fn = reply_getatr_send; + req->async.private = st; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->qpathinfo(req, st); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a setatr. +****************************************************************************/ +void reply_setatr(struct request_context *req) +{ + union smb_setfileinfo *st; + + /* parse request */ + REQ_CHECK_WCT(req, 8); + REQ_TALLOC(st, sizeof(*st)); + + st->setattr.level = RAW_SFILEINFO_SETATTR; + st->setattr.in.attrib = SVAL(req->in.vwv, VWV(0)); + st->setattr.in.write_time = make_unix_date3(req->in.vwv + VWV(1)); + + req_pull_ascii4(req, &st->setattr.file.fname, req->in.data, STR_TERMINATE); + + if (!st->setattr.file.fname) { + req_reply_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND); + return; + } + + req->async.send_fn = reply_simple_send; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->setpathinfo(req, st); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a dskattr (async reply) +****************************************************************************/ +static void reply_dskattr_send(struct request_context *req) +{ + union smb_fsinfo *fs = req->async.private; + + CHECK_ASYNC_STATUS; + + /* construct reply */ + req_setup_reply(req, 5, 0); + + SSVAL(req->out.vwv, VWV(0), fs->dskattr.out.units_total); + SSVAL(req->out.vwv, VWV(1), fs->dskattr.out.blocks_per_unit); + SSVAL(req->out.vwv, VWV(2), fs->dskattr.out.block_size); + SSVAL(req->out.vwv, VWV(3), fs->dskattr.out.units_free); + + REQ_VWV_RESERVED(4, 1); + + req_send_reply(req); +} + + +/**************************************************************************** + Reply to a dskattr. +****************************************************************************/ +void reply_dskattr(struct request_context *req) +{ + union smb_fsinfo *fs; + + REQ_TALLOC(fs, sizeof(*fs)); + + fs->dskattr.level = RAW_QFS_DSKATTR; + + req->async.send_fn = reply_dskattr_send; + req->async.private = fs; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->fsinfo(req, fs); + + REQ_ASYNC_TAIL; +} + + + +/**************************************************************************** + Reply to an open (async reply) +****************************************************************************/ +static void reply_open_send(struct request_context *req) +{ + union smb_open *oi = req->async.private; + + CHECK_ASYNC_STATUS; + + /* construct reply */ + req_setup_reply(req, 7, 0); + + SSVAL(req->out.vwv, VWV(0), oi->open.out.fnum); + SSVAL(req->out.vwv, VWV(1), oi->open.out.attrib); + put_dos_date3(req->out.vwv, VWV(2), oi->open.out.write_time); + SIVAL(req->out.vwv, VWV(4), oi->open.out.size); + SSVAL(req->out.vwv, VWV(6), oi->open.out.rmode); + + req_send_reply(req); +} + +/**************************************************************************** + Reply to an open. +****************************************************************************/ +void reply_open(struct request_context *req) +{ + union smb_open *oi; + + /* parse request */ + REQ_CHECK_WCT(req, 2); + REQ_TALLOC(oi, sizeof(*oi)); + + oi->open.level = RAW_OPEN_OPEN; + oi->open.in.flags = SVAL(req->in.vwv, VWV(0)); + oi->open.in.search_attrs = SVAL(req->in.vwv, VWV(1)); + + req_pull_ascii4(req, &oi->open.in.fname, req->in.data, STR_TERMINATE); + + if (!oi->open.in.fname) { + req_reply_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND); + return; + } + + req->async.send_fn = reply_open_send; + req->async.private = oi; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->open(req, oi); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to an open and X (async reply) +****************************************************************************/ +static void reply_open_and_X_send(struct request_context *req) +{ + union smb_open *oi = req->async.private; + + CHECK_ASYNC_STATUS; + + /* build the reply */ + if (oi->openx.in.flags & OPENX_FLAGS_EXTENDED_RETURN) { + req_setup_reply(req, 19, 0); + } else { + req_setup_reply(req, 15, 0); + } + + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + SSVAL(req->out.vwv, VWV(2), oi->openx.out.fnum); + SSVAL(req->out.vwv, VWV(3), oi->openx.out.attrib); + put_dos_date3(req->out.vwv, VWV(4), oi->openx.out.write_time); + SIVAL(req->out.vwv, VWV(6), oi->openx.out.size); + SSVAL(req->out.vwv, VWV(8), oi->openx.out.access); + SSVAL(req->out.vwv, VWV(9), oi->openx.out.ftype); + SSVAL(req->out.vwv, VWV(10),oi->openx.out.devstate); + SSVAL(req->out.vwv, VWV(11),oi->openx.out.action); + SIVAL(req->out.vwv, VWV(12),oi->openx.out.unique_fid); + SSVAL(req->out.vwv, VWV(14),0); /* reserved */ + if (oi->openx.in.flags & OPENX_FLAGS_EXTENDED_RETURN) { + SIVAL(req->out.vwv, VWV(15),oi->openx.out.access_mask); + REQ_VWV_RESERVED(17, 2); + } + + chain_reply(req); +} + + +/**************************************************************************** + Reply to an open and X. +****************************************************************************/ +void reply_open_and_X(struct request_context *req) +{ + union smb_open *oi; + + /* parse the request */ + REQ_CHECK_WCT(req, 15); + REQ_TALLOC(oi, sizeof(*oi)); + + oi->openx.level = RAW_OPEN_OPENX; + oi->openx.in.flags = SVAL(req->in.vwv, VWV(2)); + oi->openx.in.open_mode = SVAL(req->in.vwv, VWV(3)); + oi->openx.in.search_attrs = SVAL(req->in.vwv, VWV(4)); + oi->openx.in.file_attrs = SVAL(req->in.vwv, VWV(5)); + oi->openx.in.write_time = make_unix_date3(req->in.vwv + VWV(6)); + oi->openx.in.open_func = SVAL(req->in.vwv, VWV(8)); + oi->openx.in.size = IVAL(req->in.vwv, VWV(9)); + oi->openx.in.timeout = IVAL(req->in.vwv, VWV(11)); + + req_pull_ascii4(req, &oi->openx.in.fname, req->in.data, STR_TERMINATE); + + if (!oi->openx.in.fname) { + req_reply_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND); + return; + } + + req->async.send_fn = reply_open_and_X_send; + req->async.private = oi; + + /* call the backend */ + req->async.status = req->conn->ntvfs_ops->open(req, oi); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a mknew or a create. +****************************************************************************/ +static void reply_mknew_send(struct request_context *req) +{ + union smb_open *oi = req->async.private; + + CHECK_ASYNC_STATUS; + + /* build the reply */ + req_setup_reply(req, 1, 0); + + SSVAL(req->out.vwv, VWV(0), oi->mknew.out.fnum); + + req_send_reply(req); +} + + +/**************************************************************************** + Reply to a mknew or a create. +****************************************************************************/ +void reply_mknew(struct request_context *req) +{ + union smb_open *oi; + + /* parse the request */ + REQ_CHECK_WCT(req, 3); + REQ_TALLOC(oi, sizeof(*oi)); + + oi->mknew.level = RAW_OPEN_MKNEW; + oi->mknew.in.attrib = SVAL(req->in.vwv, VWV(0)); + oi->mknew.in.write_time = make_unix_date3(req->in.vwv + VWV(1)); + + req_pull_ascii4(req, &oi->mknew.in.fname, req->in.data, STR_TERMINATE); + + if (!oi->mknew.in.fname) { + req_reply_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND); + return; + } + + req->async.send_fn = reply_mknew_send; + req->async.private = oi; + + /* call the backend */ + req->async.status = req->conn->ntvfs_ops->open(req, oi); + + REQ_ASYNC_TAIL; +} + +/**************************************************************************** + Reply to a create temporary file (async reply) +****************************************************************************/ +static void reply_ctemp_send(struct request_context *req) +{ + union smb_open *oi = req->async.private; + + CHECK_ASYNC_STATUS; + + /* build the reply */ + req_setup_reply(req, 1, 0); + + SSVAL(req->out.vwv, VWV(0), oi->ctemp.out.fnum); + + /* the returned filename is relative to the directory */ + req_push_str(req, NULL, oi->ctemp.out.name, -1, STR_TERMINATE); + + req_send_reply(req); +} + +/**************************************************************************** + Reply to a create temporary file. +****************************************************************************/ +void reply_ctemp(struct request_context *req) +{ + union smb_open *oi; + + /* parse the request */ + REQ_CHECK_WCT(req, 3); + REQ_TALLOC(oi, sizeof(*oi)); + + oi->ctemp.level = RAW_OPEN_CTEMP; + oi->ctemp.in.attrib = SVAL(req->in.vwv, VWV(0)); + oi->ctemp.in.write_time = make_unix_date3(req->in.vwv + VWV(1)); + + /* the filename is actually a directory name, the server provides a filename + in that directory */ + req_pull_ascii4(req, &oi->ctemp.in.directory, req->in.data, STR_TERMINATE); + + if (!oi->ctemp.in.directory) { + req_reply_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND); + return; + } + + req->async.send_fn = reply_ctemp_send; + req->async.private = oi; + + /* call the backend */ + req->async.status = req->conn->ntvfs_ops->open(req, oi); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a unlink +****************************************************************************/ +void reply_unlink(struct request_context *req) +{ + struct smb_unlink *unl; + + /* parse the request */ + REQ_CHECK_WCT(req, 1); + REQ_TALLOC(unl, sizeof(*unl)); + + unl->in.attrib = SVAL(req->in.vwv, VWV(0)); + + req_pull_ascii4(req, &unl->in.pattern, req->in.data, STR_TERMINATE); + + req->async.send_fn = reply_simple_send; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->unlink(req, unl); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a readbraw (core+ protocol). + this is a strange packet because it doesn't use a standard SMB header in the reply, + only the 4 byte NBT header + This command must be replied to synchronously +****************************************************************************/ +void reply_readbraw(struct request_context *req) +{ + NTSTATUS status; + union smb_read io; + + io.readbraw.level = RAW_READ_READBRAW; + + /* there are two varients, one with 10 and one with 8 command words */ + if (req->in.wct != 10) { + REQ_CHECK_WCT(req, 8); + } + + io.readbraw.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); + io.readbraw.in.offset = IVAL(req->in.vwv, VWV(1)); + io.readbraw.in.mincnt = SVAL(req->in.vwv, VWV(3)); + io.readbraw.in.maxcnt = SVAL(req->in.vwv, VWV(4)); + io.readbraw.in.timeout = IVAL(req->in.vwv, VWV(5)); + + /* the 64 bit varient */ + if (req->in.wct == 10) { + uint32 offset_high = IVAL(req->in.vwv, VWV(8)); +#ifdef LARGE_SMB_OFF_T + io.readbraw.in.offset |= (((SMB_OFF_T)offset_high) << 32); +#else + if (offset_high != 0) { + goto failed; + } +#endif + } + + /* before calling the backend we setup the raw buffer. This + * saves a copy later */ + req->out.size = io.readbraw.in.maxcnt + NBT_HDR_SIZE; + req->out.buffer = talloc(req->mem_ctx, req->out.size); + if (req->out.buffer == NULL) { + goto failed; + } + + /* tell the backend where to put the data */ + io.readbraw.out.data = req->out.buffer + NBT_HDR_SIZE; + + /* call the backend */ + status = req->conn->ntvfs_ops->read(req, &io); + + if (!NT_STATUS_IS_OK(status)) { + goto failed; + } + + req->out.size = io.readbraw.out.nread + NBT_HDR_SIZE; + + req_send_reply(req); + +failed: + /* any failure in readbraw is equivalent to reading zero bytes */ + req->out.size = 4; + req->out.buffer = talloc(req->mem_ctx, req->out.size); + req_send_reply(req); +} + + +/**************************************************************************** + Reply to a lockread (async reply) +****************************************************************************/ +static void reply_lockread_send(struct request_context *req) +{ + union smb_read *io = req->async.private; + + CHECK_ASYNC_STATUS; + + /* trim packet */ + req_grow_data(req, 3 + io->lockread.out.nread); + + /* construct reply */ + SSVAL(req->out.vwv, VWV(0), io->lockread.out.nread); + REQ_VWV_RESERVED(1, 4); + + SCVAL(req->out.data, 0, SMB_DATA_BLOCK); + SSVAL(req->out.data, 1, io->lockread.out.nread); + + req_send_reply(req); +} + + +/**************************************************************************** + Reply to a lockread (core+ protocol). + note that the lock is a write lock, not a read lock! +****************************************************************************/ +void reply_lockread(struct request_context *req) +{ + union smb_read *io; + + /* parse request */ + REQ_CHECK_WCT(req, 5); + REQ_TALLOC(io, sizeof(*io)); + + io->lockread.level = RAW_READ_LOCKREAD; + io->lockread.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); + io->lockread.in.count = SVAL(req->in.vwv, VWV(1)); + io->lockread.in.offset = IVAL(req->in.vwv, VWV(2)); + io->lockread.in.remaining = SVAL(req->in.vwv, VWV(4)); + + /* setup the reply packet assuming the maximum possible read */ + req_setup_reply(req, 5, 3 + io->lockread.in.count); + + /* tell the backend where to put the data */ + io->lockread.out.data = req->out.data + 3; + + req->async.send_fn = reply_lockread_send; + req->async.private = io; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->read(req, io); + + REQ_ASYNC_TAIL; +} + + + +/**************************************************************************** + Reply to a read (async reply) +****************************************************************************/ +static void reply_read_send(struct request_context *req) +{ + union smb_read *io = req->async.private; + + CHECK_ASYNC_STATUS; + + /* trim packet */ + req_grow_data(req, 3 + io->read.out.nread); + + /* construct reply */ + SSVAL(req->out.vwv, VWV(0), io->read.out.nread); + REQ_VWV_RESERVED(1, 4); + + SCVAL(req->out.data, 0, SMB_DATA_BLOCK); + SSVAL(req->out.data, 1, io->read.out.nread); + + req_send_reply(req); +} + +/**************************************************************************** + Reply to a read. +****************************************************************************/ +void reply_read(struct request_context *req) +{ + union smb_read *io; + + /* parse request */ + REQ_CHECK_WCT(req, 5); + REQ_TALLOC(io, sizeof(*io)); + + io->read.level = RAW_READ_READ; + io->read.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); + io->read.in.count = SVAL(req->in.vwv, VWV(1)); + io->read.in.offset = IVAL(req->in.vwv, VWV(2)); + io->read.in.remaining = SVAL(req->in.vwv, VWV(4)); + + /* setup the reply packet assuming the maximum possible read */ + req_setup_reply(req, 5, 3 + io->read.in.count); + + /* tell the backend where to put the data */ + io->lockread.out.data = req->out.data + 3; + + req->async.send_fn = reply_read_send; + req->async.private = io; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->read(req, io); + + REQ_ASYNC_TAIL; +} + + + +/**************************************************************************** + Reply to a read and X (async reply) +****************************************************************************/ +static void reply_read_and_X_send(struct request_context *req) +{ + union smb_read *io = req->async.private; + + CHECK_ASYNC_STATUS; + + /* trim the packet to the right size */ + req_grow_data(req, 1 + io->readx.out.nread); + + /* construct reply */ + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + SSVAL(req->out.vwv, VWV(2), io->readx.out.remaining); + SSVAL(req->out.vwv, VWV(3), io->readx.out.compaction_mode); + REQ_VWV_RESERVED(4, 1); + SSVAL(req->out.vwv, VWV(5), io->readx.out.nread); + SSVAL(req->out.vwv, VWV(6), PTR_DIFF(io->readx.out.data, req->out.hdr)); + SCVAL(req->out.data, 0, 0); /* padding */ + REQ_VWV_RESERVED(7, 5); + + chain_reply(req); +} + +/**************************************************************************** + Reply to a read and X. +****************************************************************************/ +void reply_read_and_X(struct request_context *req) +{ + union smb_read *io; + + /* parse request */ + if (req->in.wct != 12) { + REQ_CHECK_WCT(req, 10); + } + + REQ_TALLOC(io, sizeof(*io)); + + io->readx.level = RAW_READ_READX; + io->readx.in.fnum = req_fnum(req, req->in.vwv, VWV(2)); + io->readx.in.offset = IVAL(req->in.vwv, VWV(3)); + io->readx.in.maxcnt = SVAL(req->in.vwv, VWV(5)); + io->readx.in.mincnt = SVAL(req->in.vwv, VWV(6)); + io->readx.in.remaining = SVAL(req->in.vwv, VWV(9)); + + /* the 64 bit varient */ + if (req->in.wct == 12) { + uint32 offset_high = IVAL(req->in.vwv, VWV(10)); +#ifdef LARGE_SMB_OFF_T + io->readx.in.offset |= (((SMB_OFF_T)offset_high) << 32); +#else + if (offset_high != 0) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } +#endif + } + + /* setup the reply packet assuming the maximum possible read */ + req_setup_reply(req, 12, 1 + io->readx.in.maxcnt); + + /* tell the backend where to put the data. Notice the pad byte. */ + io->readx.out.data = req->out.data + 1; + + req->async.send_fn = reply_read_and_X_send; + req->async.private = io; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->read(req, io); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a writebraw (core+ or LANMAN1.0 protocol). +****************************************************************************/ +void reply_writebraw(struct request_context *req) +{ + /* this one is damn complex - put it off for now */ + req_reply_error(req, NT_STATUS_FOOBAR); +} + + +/**************************************************************************** + Reply to a writeunlock (async reply) +****************************************************************************/ +static void reply_writeunlock_send(struct request_context *req) +{ + union smb_write *io = req->async.private; + + CHECK_ASYNC_STATUS; + + /* construct reply */ + req_setup_reply(req, 1, 0); + + SSVAL(req->out.vwv, VWV(0), io->writeunlock.out.nwritten); + + req_send_reply(req); +} + +/**************************************************************************** + Reply to a writeunlock (core+). +****************************************************************************/ +void reply_writeunlock(struct request_context *req) +{ + union smb_write *io; + + REQ_CHECK_WCT(req, 5); + REQ_TALLOC(io, sizeof(*io)); + + io->writeunlock.level = RAW_WRITE_WRITEUNLOCK; + io->writeunlock.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); + io->writeunlock.in.count = SVAL(req->in.vwv, VWV(1)); + io->writeunlock.in.offset = IVAL(req->in.vwv, VWV(2)); + io->writeunlock.in.remaining = SVAL(req->in.vwv, VWV(4)); + io->writeunlock.in.data = req->in.data + 3; + + /* make sure they gave us the data they promised */ + if (io->writeunlock.in.count+3 > req->in.data_size) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + /* make sure the data block is big enough */ + if (SVAL(req->in.data, 1) < io->writeunlock.in.count) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + req->async.send_fn = reply_writeunlock_send; + req->async.private = io; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->write(req, io); + + REQ_ASYNC_TAIL; +} + + + +/**************************************************************************** + Reply to a write (async reply) +****************************************************************************/ +static void reply_write_send(struct request_context *req) +{ + union smb_write *io = req->async.private; + + CHECK_ASYNC_STATUS; + + /* construct reply */ + req_setup_reply(req, 1, 0); + + SSVAL(req->out.vwv, VWV(0), io->write.out.nwritten); + + req_send_reply(req); +} + +/**************************************************************************** + Reply to a write +****************************************************************************/ +void reply_write(struct request_context *req) +{ + union smb_write *io; + + REQ_CHECK_WCT(req, 5); + REQ_TALLOC(io, sizeof(*io)); + + io->write.level = RAW_WRITE_WRITE; + io->write.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); + io->write.in.count = SVAL(req->in.vwv, VWV(1)); + io->write.in.offset = IVAL(req->in.vwv, VWV(2)); + io->write.in.remaining = SVAL(req->in.vwv, VWV(4)); + io->write.in.data = req->in.data + 3; + + /* make sure they gave us the data they promised */ + if (req_data_oob(req, io->write.in.data, io->write.in.count)) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + /* make sure the data block is big enough */ + if (SVAL(req->in.data, 1) < io->write.in.count) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + req->async.send_fn = reply_write_send; + req->async.private = io; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->write(req, io); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a write and X (async reply) +****************************************************************************/ +static void reply_write_and_X_send(struct request_context *req) +{ + union smb_write *io = req->async.private; + + CHECK_ASYNC_STATUS; + + /* construct reply */ + req_setup_reply(req, 6, 0); + + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + SSVAL(req->out.vwv, VWV(2), io->writex.out.nwritten & 0xFFFF); + SSVAL(req->out.vwv, VWV(3), io->writex.out.remaining); + SSVAL(req->out.vwv, VWV(4), io->writex.out.nwritten >> 16); + REQ_VWV_RESERVED(5, 1); + + chain_reply(req); +} + +/**************************************************************************** + Reply to a write and X. +****************************************************************************/ +void reply_write_and_X(struct request_context *req) +{ + union smb_write *io; + + if (req->in.wct != 14) { + REQ_CHECK_WCT(req, 12); + } + + REQ_TALLOC(io, sizeof(*io)); + + io->writex.level = RAW_WRITE_WRITEX; + io->writex.in.fnum = req_fnum(req, req->in.vwv, VWV(2)); + io->writex.in.offset = IVAL(req->in.vwv, VWV(3)); + io->writex.in.wmode = SVAL(req->in.vwv, VWV(7)); + io->writex.in.remaining = SVAL(req->in.vwv, VWV(8)); + io->writex.in.count = SVAL(req->in.vwv, VWV(10)); + io->writex.in.data = req->in.hdr + SVAL(req->in.vwv, VWV(11)); + + if (req->in.wct == 14) { + uint32 offset_high = IVAL(req->in.vwv, VWV(12)); + uint16 count_high = SVAL(req->in.vwv, VWV(9)); +#ifdef LARGE_SMB_OFF_T + io->writex.in.offset |= (((SMB_OFF_T)offset_high) << 32); +#else + if (offset_high != 0) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } +#endif + io->writex.in.count |= ((uint32)count_high) << 16; + } + + /* make sure the data is in bounds */ + if (req_data_oob(req, io->writex.in.data, io->writex.in.count)) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + req->async.send_fn = reply_write_and_X_send; + req->async.private = io; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->write(req, io); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a lseek (async reply) +****************************************************************************/ +static void reply_lseek_send(struct request_context *req) +{ + struct smb_seek *io = req->async.private; + + CHECK_ASYNC_STATUS; + + /* construct reply */ + req_setup_reply(req, 2, 0); + + SIVALS(req->out.vwv, VWV(0), io->out.offset); + + req_send_reply(req); +} + +/**************************************************************************** + Reply to a lseek. +****************************************************************************/ +void reply_lseek(struct request_context *req) +{ + struct smb_seek *io; + + REQ_CHECK_WCT(req, 4); + REQ_TALLOC(io, sizeof(*io)); + + io->in.fnum = req_fnum(req, req->in.vwv, VWV(0)); + io->in.mode = SVAL(req->in.vwv, VWV(1)); + io->in.offset = IVALS(req->in.vwv, VWV(2)); + + req->async.send_fn = reply_lseek_send; + req->async.private = io; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->seek(req, io); + + REQ_ASYNC_TAIL; +} + +/**************************************************************************** + Reply to a flush. +****************************************************************************/ +void reply_flush(struct request_context *req) +{ + struct smb_flush *io; + + /* parse request */ + REQ_CHECK_WCT(req, 1); + REQ_TALLOC(io, sizeof(*io)); + + io->in.fnum = req_fnum(req, req->in.vwv, VWV(0)); + + req->async.send_fn = reply_simple_send; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->flush(req, io); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a exit. +****************************************************************************/ +void reply_exit(struct request_context *req) +{ + REQ_CHECK_WCT(req, 0); + + req->async.send_fn = reply_simple_send; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->exit(req); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a close + + Note that this has to deal with closing a directory opened by NT SMB's. +****************************************************************************/ +void reply_close(struct request_context *req) +{ + union smb_close *io; + + /* parse request */ + REQ_CHECK_WCT(req, 3); + REQ_TALLOC(io, sizeof(*io)); + + io->close.level = RAW_CLOSE_CLOSE; + io->close.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); + io->close.in.write_time = make_unix_date3(req->in.vwv + VWV(1)); + + req->async.send_fn = reply_simple_send; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->close(req, io); + + REQ_ASYNC_TAIL; +} + + + +/**************************************************************************** + Reply to a writeclose (async reply) +****************************************************************************/ +static void reply_writeclose_send(struct request_context *req) +{ + union smb_write *io = req->async.private; + + CHECK_ASYNC_STATUS; + + /* construct reply */ + req_setup_reply(req, 1, 0); + + SSVAL(req->out.vwv, VWV(0), io->write.out.nwritten); + + req_send_reply(req); +} + +/**************************************************************************** + Reply to a writeclose (Core+ protocol). +****************************************************************************/ +void reply_writeclose(struct request_context *req) +{ + union smb_write *io; + + /* this one is pretty weird - the wct can be 6 or 12 */ + if (req->in.wct != 12) { + REQ_CHECK_WCT(req, 6); + } + + REQ_TALLOC(io, sizeof(*io)); + + io->writeclose.level = RAW_WRITE_WRITECLOSE; + io->writeclose.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); + io->writeclose.in.count = SVAL(req->in.vwv, VWV(1)); + io->writeclose.in.offset = IVAL(req->in.vwv, VWV(2)); + io->writeclose.in.mtime = make_unix_date3(req->in.vwv + VWV(4)); + io->writeclose.in.data = req->in.data + 1; + + /* make sure they gave us the data they promised */ + if (req_data_oob(req, io->writeclose.in.data, io->writeclose.in.count)) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + req->async.send_fn = reply_writeclose_send; + req->async.private = io; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->write(req, io); + + REQ_ASYNC_TAIL; +} + +/**************************************************************************** + Reply to a lock. +****************************************************************************/ +void reply_lock(struct request_context *req) +{ + union smb_lock *lck; + + /* parse request */ + REQ_CHECK_WCT(req, 5); + REQ_TALLOC(lck, sizeof(*lck)); + + lck->lock.level = RAW_LOCK_LOCK; + lck->lock.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); + lck->lock.in.count = IVAL(req->in.vwv, VWV(1)); + lck->lock.in.offset = IVAL(req->in.vwv, VWV(3)); + + req->async.send_fn = reply_simple_send; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->lock(req, lck); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a unlock. +****************************************************************************/ +void reply_unlock(struct request_context *req) +{ + union smb_lock *lck; + + /* parse request */ + REQ_CHECK_WCT(req, 5); + REQ_TALLOC(lck, sizeof(*lck)); + + lck->unlock.level = RAW_LOCK_UNLOCK; + lck->unlock.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); + lck->unlock.in.count = IVAL(req->in.vwv, VWV(1)); + lck->unlock.in.offset = IVAL(req->in.vwv, VWV(3)); + + req->async.send_fn = reply_simple_send; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->lock(req, lck); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a tdis. +****************************************************************************/ +void reply_tdis(struct request_context *req) +{ + REQ_CHECK_WCT(req, 0); + + close_cnum(req->conn); + + /* construct reply */ + req_setup_reply(req, 0, 0); + + req_send_reply(req); +} + + +/**************************************************************************** + Reply to a echo. This is one of the few calls that is handled directly (the + backends don't see it at all) +****************************************************************************/ +void reply_echo(struct request_context *req) +{ + uint16 count; + int i; + + REQ_CHECK_WCT(req, 0); + + count = SVAL(req->in.vwv, VWV(0)); + + req_setup_reply(req, 1, req->in.data_size); + + memcpy(req->out.data, req->in.data, req->in.data_size); + + /* we need to make sure the request isn't destroyed till the + * last packet */ + req->control_flags |= REQ_CONTROL_PROTECTED; + + for (i=1; i <= count;i++) { + if (i == count) { + req->control_flags &= ~REQ_CONTROL_PROTECTED; + } + + SSVAL(req->out.vwv, VWV(0), i); + req_send_reply(req); + } +} + + + +/**************************************************************************** + Reply to a printopen (async reply) +****************************************************************************/ +static void reply_printopen_send(struct request_context *req) +{ + union smb_open *oi = req->async.private; + + CHECK_ASYNC_STATUS; + + /* construct reply */ + req_setup_reply(req, 1, 0); + + SSVAL(req->out.vwv, VWV(0), oi->open.out.fnum); + + req_send_reply(req); +} + +/**************************************************************************** + Reply to a printopen. +****************************************************************************/ +void reply_printopen(struct request_context *req) +{ + union smb_open *oi; + + /* parse request */ + REQ_CHECK_WCT(req, 2); + REQ_TALLOC(oi, sizeof(*oi)); + + oi->splopen.level = RAW_OPEN_SPLOPEN; + oi->splopen.in.setup_length = SVAL(req->in.vwv, VWV(0)); + oi->splopen.in.mode = SVAL(req->in.vwv, VWV(1)); + + req_pull_ascii4(req, &oi->splopen.in.ident, req->in.data, STR_TERMINATE); + + req->async.send_fn = reply_printopen_send; + req->async.private = oi; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->open(req, oi); + + REQ_ASYNC_TAIL; +} + +/**************************************************************************** + Reply to a printclose. +****************************************************************************/ +void reply_printclose(struct request_context *req) +{ + union smb_close *io; + + /* parse request */ + REQ_CHECK_WCT(req, 3); + REQ_TALLOC(io, sizeof(*io)); + + io->splclose.level = RAW_CLOSE_SPLCLOSE; + io->splclose.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); + + req->async.send_fn = reply_simple_send; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->close(req, io); + + REQ_ASYNC_TAIL; +} + +/**************************************************************************** + Reply to a printqueue. +****************************************************************************/ +void reply_printqueue_send(struct request_context *req) +{ + union smb_lpq *lpq = req->async.private; + int i, maxcount; + const uint_t el_size = 28; + + CHECK_ASYNC_STATUS; + + /* construct reply */ + req_setup_reply(req, 2, 0); + + /* truncate the returned list to fit in the negotiated buffer size */ + maxcount = (req_max_data(req) - 3) / el_size; + if (maxcount < lpq->retq.out.count) { + lpq->retq.out.count = maxcount; + } + + /* setup enough space in the reply */ + req_grow_data(req, 3 + el_size*lpq->retq.out.count); + + /* and fill it in */ + SSVAL(req->out.vwv, VWV(0), lpq->retq.out.count); + SSVAL(req->out.vwv, VWV(1), lpq->retq.out.restart_idx); + + SCVAL(req->out.data, 0, SMB_DATA_BLOCK); + SSVAL(req->out.data, 1, el_size*lpq->retq.out.count); + + req->out.ptr = req->out.data + 3; + + for (i=0;iretq.out.count;i++) { + put_dos_date2(req->out.ptr, 0 , lpq->retq.out.queue[i].time); + SCVAL(req->out.ptr, 4, lpq->retq.out.queue[i].status); + SSVAL(req->out.ptr, 5, lpq->retq.out.queue[i].job); + SIVAL(req->out.ptr, 7, lpq->retq.out.queue[i].size); + SCVAL(req->out.ptr, 11, 0); /* reserved */ + req_push_str(req, req->out.ptr+12, lpq->retq.out.queue[i].user, 16, STR_ASCII); + req->out.ptr += el_size; + } + + req_send_reply(req); +} + +/**************************************************************************** + Reply to a printqueue. +****************************************************************************/ +void reply_printqueue(struct request_context *req) +{ + union smb_lpq *lpq; + + /* parse request */ + REQ_CHECK_WCT(req, 2); + REQ_TALLOC(lpq, sizeof(*lpq)); + + lpq->retq.level = RAW_LPQ_RETQ; + lpq->retq.in.maxcount = SVAL(req->in.vwv, VWV(0)); + lpq->retq.in.startidx = SVAL(req->in.vwv, VWV(1)); + + req->async.send_fn = reply_printqueue_send; + req->async.private = lpq; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->lpq(req, lpq); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a printwrite. +****************************************************************************/ +void reply_printwrite(struct request_context *req) +{ + union smb_write *io; + + /* parse request */ + REQ_CHECK_WCT(req, 1); + REQ_TALLOC(io, sizeof(*io)); + + io->splwrite.level = RAW_WRITE_SPLWRITE; + + if (req->in.data_size < 3) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + io->splwrite.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); + io->splwrite.in.count = SVAL(req->in.data, 1); + io->splwrite.in.data = req->in.data + 3; + + /* make sure they gave us the data they promised */ + if (req_data_oob(req, io->splwrite.in.data, io->splwrite.in.count)) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + req->async.send_fn = reply_simple_send; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->write(req, io); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a mkdir. +****************************************************************************/ +void reply_mkdir(struct request_context *req) +{ + union smb_mkdir *io; + + /* parse the request */ + REQ_CHECK_WCT(req, 0); + REQ_TALLOC(io, sizeof(*io)); + + io->generic.level = RAW_MKDIR_MKDIR; + req_pull_ascii4(req, &io->mkdir.in.path, req->in.data, STR_TERMINATE); + + req->async.send_fn = reply_simple_send; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->mkdir(req, io); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a rmdir. +****************************************************************************/ +void reply_rmdir(struct request_context *req) +{ + struct smb_rmdir *io; + + /* parse the request */ + REQ_CHECK_WCT(req, 0); + REQ_TALLOC(io, sizeof(*io)); + + req_pull_ascii4(req, &io->in.path, req->in.data, STR_TERMINATE); + + req->async.send_fn = reply_simple_send; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->rmdir(req, io); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a mv. +****************************************************************************/ +void reply_mv(struct request_context *req) +{ + struct smb_rename *io; + char *p; + + /* parse the request */ + REQ_CHECK_WCT(req, 1); + REQ_TALLOC(io, sizeof(*io)); + + io->in.attrib = SVAL(req->in.vwv, VWV(0)); + + p = req->in.data; + p += req_pull_ascii4(req, &io->in.pattern1, p, STR_TERMINATE); + p += req_pull_ascii4(req, &io->in.pattern2, p, STR_TERMINATE); + + if (!io->in.pattern1 || !io->in.pattern2) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + req->async.send_fn = reply_simple_send; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->rename(req, io); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a file copy (async reply) +****************************************************************************/ +static void reply_copy_send(struct request_context *req) +{ + struct smb_copy *cp = req->async.private; + + CHECK_ASYNC_STATUS; + + /* build the reply */ + req_setup_reply(req, 1, 0); + + SSVAL(req->out.vwv, VWV(0), cp->out.count); + + req_send_reply(req); +} + +/**************************************************************************** + Reply to a file copy. +****************************************************************************/ +void reply_copy(struct request_context *req) +{ + struct smb_copy *cp; + char *p; + + /* parse request */ + REQ_CHECK_WCT(req, 3); + REQ_TALLOC(cp, sizeof(*cp)); + + cp->in.tid2 = SVAL(req->in.vwv, VWV(0)); + cp->in.ofun = SVAL(req->in.vwv, VWV(1)); + cp->in.flags = SVAL(req->in.vwv, VWV(2)); + + p = req->in.data; + p += req_pull_ascii4(req, &cp->in.path1, p, STR_TERMINATE); + p += req_pull_ascii4(req, &cp->in.path2, p, STR_TERMINATE); + + if (!cp->in.path1 || !cp->in.path2) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + req->async.send_fn = reply_copy_send; + req->async.private = cp; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->copy(req, cp); + + REQ_ASYNC_TAIL; +} + +/**************************************************************************** + Reply to a lockingX request (async send) +****************************************************************************/ +static void reply_lockingX_send(struct request_context *req) +{ + union smb_lock *lck = req->async.private; + + CHECK_ASYNC_STATUS; + + /* if it was an oplock break ack then we only send a reply if + there was an error */ + if (lck->lockx.in.ulock_cnt + lck->lockx.in.lock_cnt == 0) { + req_destroy(req); + return; + } + + /* construct reply */ + req_setup_reply(req, 2, 0); + + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + + chain_reply(req); +} + + +/**************************************************************************** + Reply to a lockingX request. +****************************************************************************/ +void reply_lockingX(struct request_context *req) +{ + union smb_lock *lck; + uint_t total_locks, i; + uint_t lck_size; + char *p; + + /* parse request */ + REQ_CHECK_WCT(req, 8); + REQ_TALLOC(lck, sizeof(*lck)); + + lck->lockx.level = RAW_LOCK_LOCKX; + lck->lockx.in.fnum = req_fnum(req, req->in.vwv, VWV(2)); + lck->lockx.in.mode = SVAL(req->in.vwv, VWV(3)); + lck->lockx.in.timeout = IVAL(req->in.vwv, VWV(4)); + lck->lockx.in.ulock_cnt = SVAL(req->in.vwv, VWV(6)); + lck->lockx.in.lock_cnt = SVAL(req->in.vwv, VWV(7)); + + total_locks = lck->lockx.in.ulock_cnt + lck->lockx.in.lock_cnt; + + /* there are two varients, one with 64 bit offsets and counts */ + if (lck->lockx.in.mode & LOCKING_ANDX_LARGE_FILES) { + lck_size = 20; + } else { + lck_size = 10; + } + + /* make sure we got the promised data */ + if (req_data_oob(req, req->in.data, total_locks * lck_size)) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + /* allocate the locks array */ + if (total_locks) { + REQ_TALLOC(lck->lockx.in.locks, total_locks * sizeof(lck->lockx.in.locks[0])); + } + + p = req->in.data; + + /* construct the locks array */ + for (i=0;ilockx.in.locks[i].pid = SVAL(p, 0); + + if (lck->lockx.in.mode & LOCKING_ANDX_LARGE_FILES) { + ofs_high = IVAL(p, 4); + lck->lockx.in.locks[i].offset = IVAL(p, 8); + count_high = IVAL(p, 12); + lck->lockx.in.locks[i].count = IVAL(p, 16); + } else { + lck->lockx.in.locks[i].offset = IVAL(p, 2); + lck->lockx.in.locks[i].count = IVAL(p, 6); + } + if (ofs_high != 0 || count_high != 0) { +#ifdef LARGE_SMB_OFF_T + lck->lockx.in.locks[i].count |= ((SMB_OFF_T)count_high) << 32; + lck->lockx.in.locks[i].offset |= ((SMB_OFF_T)ofs_high) << 32; +#else + req_reply_error(req, NT_STATUS_FOOBAR); + return; +#endif + } + p += lck_size; + } + + req->async.send_fn = reply_lockingX_send; + req->async.private = lck; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->lock(req, lck); + + REQ_ASYNC_TAIL; +} + +/**************************************************************************** + Reply to a SMBreadbmpx (read block multiplex) request. +****************************************************************************/ +void reply_readbmpx(struct request_context *req) +{ + /* tell the client to not use a multiplexed read - its too broken to use */ + req_reply_dos_error(req, ERRSRV, ERRuseSTD); +} + + +/**************************************************************************** + Reply to a SMBsetattrE. +****************************************************************************/ +void reply_setattrE(struct request_context *req) +{ + union smb_setfileinfo *info; + + /* parse request */ + REQ_CHECK_WCT(req, 7); + REQ_TALLOC(info, sizeof(*info)); + + info->setattre.level = RAW_SFILEINFO_SETATTRE; + info->setattre.file.fnum = req_fnum(req, req->in.vwv, VWV(0)); + info->setattre.in.create_time = make_unix_date2(req->in.vwv + VWV(1)); + info->setattre.in.access_time = make_unix_date2(req->in.vwv + VWV(3)); + info->setattre.in.write_time = make_unix_date2(req->in.vwv + VWV(5)); + + req->async.send_fn = reply_simple_send; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->setfileinfo(req, info); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to a SMBwritebmpx (write block multiplex primary) request. +****************************************************************************/ +void reply_writebmpx(struct request_context *req) +{ + /* we will need to implement this one for OS/2, but right now I can't be bothered */ + req_reply_error(req, NT_STATUS_FOOBAR); +} + + +/**************************************************************************** + Reply to a SMBwritebs (write block multiplex secondary) request. +****************************************************************************/ +void reply_writebs(struct request_context *req) +{ + /* see reply_writebmpx */ + req_reply_error(req, NT_STATUS_FOOBAR); +} + + + +/**************************************************************************** + Reply to a SMBgetattrE (async reply) +****************************************************************************/ +static void reply_getattrE_send(struct request_context *req) +{ + union smb_fileinfo *info = req->async.private; + + CHECK_ASYNC_STATUS; + + /* setup reply */ + req_setup_reply(req, 11, 0); + + put_dos_date2(req->out.vwv, VWV(0), info->getattre.out.create_time); + put_dos_date2(req->out.vwv, VWV(2), info->getattre.out.access_time); + put_dos_date2(req->out.vwv, VWV(4), info->getattre.out.write_time); + SIVAL(req->out.vwv, VWV(6), info->getattre.out.size); + SIVAL(req->out.vwv, VWV(8), info->getattre.out.alloc_size); + SSVAL(req->out.vwv, VWV(10), info->getattre.out.attrib); + + req_send_reply(req); +} + +/**************************************************************************** + Reply to a SMBgetattrE. +****************************************************************************/ +void reply_getattrE(struct request_context *req) +{ + union smb_fileinfo *info; + + /* parse request */ + REQ_CHECK_WCT(req, 1); + REQ_TALLOC(info, sizeof(*info)); + + info->getattr.level = RAW_FILEINFO_GETATTRE; + info->getattr.in.fnum = req_fnum(req, req->in.vwv, VWV(0)); + + req->async.send_fn = reply_getattrE_send; + req->async.private = info; + + /* call backend */ + req->async.status = req->conn->ntvfs_ops->qfileinfo(req, info); + + REQ_ASYNC_TAIL; +} + +/**************************************************************************** + Reply to a search. + Can be called from SMBsearch, SMBffirst or SMBfunique. +****************************************************************************/ +void reply_search(struct request_context *req) +{ + /* do this one later */ + req_reply_error(req, NT_STATUS_FOOBAR); +} + + +/**************************************************************************** + Reply to a fclose (stop directory search). +****************************************************************************/ +void reply_fclose(struct request_context *req) +{ + /* skip this one for now too */ + req_reply_error(req, NT_STATUS_FOOBAR); +} + + +/**************************************************************************** +reply to an old style session setup command +****************************************************************************/ +static void reply_sesssetup_old(struct request_context *req) +{ + NTSTATUS status; + union smb_sesssetup sess; + char *p; + uint16 passlen; + + sess.old.level = RAW_SESSSETUP_OLD; + + /* parse request */ + sess.old.in.bufsize = SVAL(req->in.vwv, VWV(2)); + sess.old.in.mpx_max = SVAL(req->in.vwv, VWV(3)); + sess.old.in.vc_num = SVAL(req->in.vwv, VWV(4)); + sess.old.in.sesskey = IVAL(req->in.vwv, VWV(5)); + passlen = SVAL(req->in.vwv, VWV(7)); + + /* check the request isn't malformed */ + if (req_data_oob(req, req->in.data, passlen)) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + p = req->in.data; + if (!req_pull_blob(req, p, passlen, &sess.old.in.password)) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + p += passlen; + + p += req_pull_string(req, &sess.old.in.user, p, -1, STR_TERMINATE); + p += req_pull_string(req, &sess.old.in.domain, p, -1, STR_TERMINATE); + p += req_pull_string(req, &sess.old.in.os, p, -1, STR_TERMINATE); + p += req_pull_string(req, &sess.old.in.lanman, p, -1, STR_TERMINATE); + + /* call the generic handler */ + status = sesssetup_backend(req, &sess); + + if (!NT_STATUS_IS_OK(status)) { + req_reply_error(req, status); + return; + } + + /* construct reply */ + req_setup_reply(req, 3, 0); + + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + SSVAL(req->out.vwv, VWV(2), sess.old.out.action); + + SSVAL(req->out.hdr, HDR_UID, sess.old.out.vuid); + + chain_reply(req); +} + + +/**************************************************************************** +reply to an NT1 style session setup command +****************************************************************************/ +static void reply_sesssetup_nt1(struct request_context *req) +{ + NTSTATUS status; + union smb_sesssetup sess; + char *p; + uint16 passlen1, passlen2; + + sess.nt1.level = RAW_SESSSETUP_NT1; + + /* parse request */ + sess.nt1.in.bufsize = SVAL(req->in.vwv, VWV(2)); + sess.nt1.in.mpx_max = SVAL(req->in.vwv, VWV(3)); + sess.nt1.in.vc_num = SVAL(req->in.vwv, VWV(4)); + sess.nt1.in.sesskey = IVAL(req->in.vwv, VWV(5)); + passlen1 = SVAL(req->in.vwv, VWV(7)); + passlen2 = SVAL(req->in.vwv, VWV(8)); + sess.nt1.in.capabilities = IVAL(req->in.vwv, VWV(11)); + + /* check the request isn't malformed */ + if (req_data_oob(req, req->in.data, passlen1) || + req_data_oob(req, req->in.data + passlen1, passlen2)) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + p = req->in.data; + if (!req_pull_blob(req, p, passlen1, &sess.nt1.in.password1)) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + p += passlen1; + if (!req_pull_blob(req, p, passlen2, &sess.nt1.in.password2)) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + p += passlen2; + + p += req_pull_string(req, &sess.nt1.in.user, p, -1, STR_TERMINATE); + p += req_pull_string(req, &sess.nt1.in.domain, p, -1, STR_TERMINATE); + p += req_pull_string(req, &sess.nt1.in.os, p, -1, STR_TERMINATE); + p += req_pull_string(req, &sess.nt1.in.lanman, p, -1, STR_TERMINATE); + + /* call the generic handler */ + status = sesssetup_backend(req, &sess); + + if (!NT_STATUS_IS_OK(status)) { + req_reply_error(req, status); + return; + } + + /* construct reply */ + req_setup_reply(req, 3, 0); + + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + SSVAL(req->out.vwv, VWV(2), sess.nt1.out.action); + + SSVAL(req->out.hdr, HDR_UID, sess.nt1.out.vuid); + + req_push_str(req, NULL, sess.nt1.out.os, -1, STR_TERMINATE); + req_push_str(req, NULL, sess.nt1.out.lanman, -1, STR_TERMINATE); + req_push_str(req, NULL, sess.nt1.out.domain, -1, STR_TERMINATE); + + chain_reply(req); +} + + +/**************************************************************************** +reply to an SPNEGO style session setup command +****************************************************************************/ +static void reply_sesssetup_spnego(struct request_context *req) +{ + NTSTATUS status; + union smb_sesssetup sess; + char *p; + uint16 blob_len; + + sess.spnego.level = RAW_SESSSETUP_SPNEGO; + + /* parse request */ + sess.spnego.in.bufsize = SVAL(req->in.vwv, VWV(2)); + sess.spnego.in.mpx_max = SVAL(req->in.vwv, VWV(3)); + sess.spnego.in.vc_num = SVAL(req->in.vwv, VWV(4)); + sess.spnego.in.sesskey = IVAL(req->in.vwv, VWV(5)); + blob_len = SVAL(req->in.vwv, VWV(7)); + sess.spnego.in.capabilities = IVAL(req->in.vwv, VWV(10)); + + p = req->in.data; + if (!req_pull_blob(req, p, blob_len, &sess.spnego.in.secblob)) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + p += blob_len; + + p += req_pull_string(req, &sess.spnego.in.os, p, -1, STR_TERMINATE); + p += req_pull_string(req, &sess.spnego.in.lanman, p, -1, STR_TERMINATE); + p += req_pull_string(req, &sess.spnego.in.domain, p, -1, STR_TERMINATE); + + /* call the generic handler */ + status = sesssetup_backend(req, &sess); + + if (!NT_STATUS_IS_OK(status)) { + req_reply_error(req, status); + return; + } + + /* construct reply */ + req_setup_reply(req, 4, sess.spnego.out.secblob.length); + + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + SSVAL(req->out.vwv, VWV(2), sess.spnego.out.action); + SSVAL(req->out.vwv, VWV(3), sess.spnego.out.secblob.length); + + SSVAL(req->out.hdr, HDR_UID, sess.spnego.out.vuid); + + memcpy(req->out.data, sess.spnego.out.secblob.data, sess.spnego.out.secblob.length); + req_push_str(req, NULL, sess.spnego.out.os, -1, STR_TERMINATE); + req_push_str(req, NULL, sess.spnego.out.lanman, -1, STR_TERMINATE); + + chain_reply(req); +} + + +/**************************************************************************** +reply to a session setup command +****************************************************************************/ +void reply_sesssetup(struct request_context *req) +{ + switch (req->in.wct) { + case 10: + /* a pre-NT1 call */ + reply_sesssetup_old(req); + return; + case 13: + /* a NT1 call */ + reply_sesssetup_nt1(req); + return; + case 12: + /* a SPNEGO call */ + reply_sesssetup_spnego(req); + return; + } + + /* unsupported varient */ + req_reply_error(req, NT_STATUS_FOOBAR); +} + + +/**************************************************************************** + Reply to a SMBulogoffX. +****************************************************************************/ +void reply_ulogoffX(struct request_context *req) +{ + uint16 vuid; + + vuid = SVAL(req->in.hdr, HDR_UID); + + /* in user level security we are supposed to close any files + open by this user */ + if ((vuid != 0) && (lp_security() != SEC_SHARE)) { + DEBUG(0,("REWRITE: not closing user files\n")); + } + + invalidate_vuid(req->smb, vuid); + + req_setup_reply(req, 2, 0); + + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + + chain_reply(req); +} + + +/**************************************************************************** + Reply to an SMBtrans request +****************************************************************************/ +void reply_trans(struct request_context *req) +{ + req_reply_error(req, NT_STATUS_FOOBAR); +} + + +/**************************************************************************** + Reply to an SMBtranss2 request +****************************************************************************/ +void reply_transs2(struct request_context *req) +{ + req_reply_error(req, NT_STATUS_FOOBAR); +} + +/**************************************************************************** + Reply to an SMBnttrans request +****************************************************************************/ +void reply_nttrans(struct request_context *req) +{ + req_reply_error(req, NT_STATUS_FOOBAR); +} + +/**************************************************************************** + Reply to an SMBnttranss request +****************************************************************************/ +void reply_nttranss(struct request_context *req) +{ + req_reply_error(req, NT_STATUS_FOOBAR); +} + + +/**************************************************************************** + Reply to an SMBfindclose request +****************************************************************************/ +void reply_findclose(struct request_context *req) +{ + NTSTATUS status; + union smb_search_close io; + + io.findclose.level = RAW_FINDCLOSE_CLOSE; + + /* parse request */ + REQ_CHECK_WCT(req, 1); + + io.findclose.in.handle = SVAL(req->in.vwv, VWV(0)); + + /* call backend */ + status = req->conn->ntvfs_ops->search_close(req, &io); + + if (!NT_STATUS_IS_OK(status)) { + req_reply_error(req, status); + return; + } + + /* construct reply */ + req_setup_reply(req, 0, 0); + + req_send_reply(req); +} + +/**************************************************************************** + Reply to an SMBfindnclose request +****************************************************************************/ +void reply_findnclose(struct request_context *req) +{ + req_reply_error(req, NT_STATUS_FOOBAR); +} + + +/**************************************************************************** + Reply to an SMBntcreateX request (async send) +****************************************************************************/ +static void reply_ntcreate_and_X_send(struct request_context *req) +{ + union smb_open *io = req->async.private; + + CHECK_ASYNC_STATUS; + + /* construct reply */ + req_setup_reply(req, 34, 0); + + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + SCVAL(req->out.vwv, VWV(2), io->ntcreatex.out.oplock_level); + + /* the rest of the parameters are not aligned! */ + SSVAL(req->out.vwv, 5, io->ntcreatex.out.fnum); + SIVAL(req->out.vwv, 7, io->ntcreatex.out.create_action); + push_nttime(req->out.vwv, 11, &io->ntcreatex.out.create_time); + push_nttime(req->out.vwv, 19, &io->ntcreatex.out.access_time); + push_nttime(req->out.vwv, 27, &io->ntcreatex.out.write_time); + push_nttime(req->out.vwv, 35, &io->ntcreatex.out.change_time); + SIVAL(req->out.vwv, 43, io->ntcreatex.out.attrib); + SBVAL(req->out.vwv, 47, io->ntcreatex.out.alloc_size); + SBVAL(req->out.vwv, 55, io->ntcreatex.out.size); + SSVAL(req->out.vwv, 63, io->ntcreatex.out.file_type); + SSVAL(req->out.vwv, 65, io->ntcreatex.out.ipc_state); + SCVAL(req->out.vwv, 67, io->ntcreatex.out.is_directory); + + chain_reply(req); +} + +/**************************************************************************** + Reply to an SMBntcreateX request +****************************************************************************/ +void reply_ntcreate_and_X(struct request_context *req) +{ + union smb_open *io; + uint16 fname_len; + + /* parse the request */ + REQ_CHECK_WCT(req, 24); + REQ_TALLOC(io, sizeof(*io)); + + io->ntcreatex.level = RAW_OPEN_NTCREATEX; + + /* notice that the word parameters are not word aligned, so we don't use VWV() */ + fname_len = SVAL(req->in.vwv, 5); + io->ntcreatex.in.flags = IVAL(req->in.vwv, 7); + io->ntcreatex.in.root_fid = IVAL(req->in.vwv, 11); + io->ntcreatex.in.access_mask = IVAL(req->in.vwv, 15); + io->ntcreatex.in.alloc_size = BVAL(req->in.vwv, 19); + io->ntcreatex.in.file_attr = IVAL(req->in.vwv, 27); + io->ntcreatex.in.share_access = IVAL(req->in.vwv, 31); + io->ntcreatex.in.open_disposition = IVAL(req->in.vwv, 35); + io->ntcreatex.in.create_options = IVAL(req->in.vwv, 39); + io->ntcreatex.in.impersonation = IVAL(req->in.vwv, 43); + io->ntcreatex.in.security_flags = CVAL(req->in.vwv, 47); + + /* we need a neater way to handle this alignment */ + if ((req->flags2 & FLAGS2_UNICODE_STRINGS) && + ucs2_align(req->in.buffer, req->in.data, STR_TERMINATE|STR_UNICODE)) { + fname_len++; + } + + req_pull_string(req, &io->ntcreatex.in.fname, req->in.data, fname_len, STR_TERMINATE); + if (!io->ntcreatex.in.fname) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + req->async.send_fn = reply_ntcreate_and_X_send; + req->async.private = io; + + /* call the backend */ + req->async.status = req->conn->ntvfs_ops->open(req, io); + + REQ_ASYNC_TAIL; +} + + +/**************************************************************************** + Reply to an SMBntcancel request +****************************************************************************/ +void reply_ntcancel(struct request_context *req) +{ + req_reply_error(req, NT_STATUS_FOOBAR); +} + +/**************************************************************************** + Reply to an SMBsends request +****************************************************************************/ +void reply_sends(struct request_context *req) +{ + req_reply_error(req, NT_STATUS_FOOBAR); +} + +/**************************************************************************** + Reply to an SMBsendstrt request +****************************************************************************/ +void reply_sendstrt(struct request_context *req) +{ + req_reply_error(req, NT_STATUS_FOOBAR); +} + +/**************************************************************************** + Reply to an SMBsendend request +****************************************************************************/ +void reply_sendend(struct request_context *req) +{ + req_reply_error(req, NT_STATUS_FOOBAR); +} + +/**************************************************************************** + Reply to an SMBsendtxt request +****************************************************************************/ +void reply_sendtxt(struct request_context *req) +{ + req_reply_error(req, NT_STATUS_FOOBAR); +} + + + +/**************************************************************************** + Reply to a special message - a SMB packet with non zero NBT message type +****************************************************************************/ +void reply_special(struct request_context *req) +{ + uint8 msg_type; + char buf[4]; + + msg_type = CVAL(req->in.buffer,0); + + SIVAL(buf, 0, 0); + + switch (msg_type) { + case 0x81: /* session request */ + if (req->smb->negotiate.done_nbt_session) { + exit_server(req->smb, "multiple session request not permitted"); + } + + SCVAL(buf,0,0x82); + SCVAL(buf,3,0); + + DEBUG(0,("REWRITE: not parsing netbios names in NBT session request!\n")); + + req->smb->negotiate.done_nbt_session = True; + + req->out.buffer = buf; + req->out.size = 4; + req_send_reply(req); + return; + + case 0x89: /* session keepalive request + (some old clients produce this?) */ + SCVAL(buf, 0, SMBkeepalive); + SCVAL(buf, 3, 0); + req->out.buffer = buf; + req->out.size = 4; + req_send_reply(req); + return; + + case SMBkeepalive: + /* session keepalive - swallow it */ + req_destroy(req); + return; + } + + DEBUG(0,("Unexpected NBT session packet (%d)\n", msg_type)); + req_destroy(req); +} diff --git a/source4/smbd/request.c b/source4/smbd/request.c new file mode 100644 index 0000000000..564bb07f90 --- /dev/null +++ b/source4/smbd/request.c @@ -0,0 +1,571 @@ +/* + Unix SMB/CIFS implementation. + + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the 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 implements functions for manipulating the 'struct request_context' structure in smbd +*/ + +#include "includes.h" + +/* we over allocate the data buffer to prevent too many realloc calls */ +#define REQ_OVER_ALLOCATION 256 + +/* destroy a request structure */ +void req_destroy(struct request_context *req) +{ + /* the request might be marked protected. This is done by the + * SMBecho code for example */ + if (req->control_flags & REQ_CONTROL_PROTECTED) { + return; + } + + /* ahh, its so nice to destroy a complex structure in such a + * simple way! */ + talloc_destroy(req->mem_ctx); +} + +/**************************************************************************** +construct a basic request packet, mostly used to construct async packets +such as change notify and oplock break requests +****************************************************************************/ +struct request_context *init_smb_request(struct server_context *smb) +{ + struct request_context *req; + TALLOC_CTX *mem_ctx; + + /* each request gets its own talloc context. The request + structure itself is also allocated inside this context, so + we need to allocate it before we construct the request + */ + mem_ctx = talloc_init("request_context[%d]", smb->socket.pkt_count); + if (!mem_ctx) { + return NULL; + } + + smb->socket.pkt_count++; + + req = talloc(mem_ctx, sizeof(*req)); + ZERO_STRUCTP(req); + + /* setup the request context */ + req->smb = smb; + req->mem_ctx = mem_ctx; + + return req; +} + + +/* + setup a chained reply in req->out with the given word count and initial data buffer size. +*/ +static void req_setup_chain_reply(struct request_context *req, unsigned wct, unsigned buflen) +{ + uint32 chain_base_size = req->out.size; + + /* we need room for the wct value, the words, the buffer length and the buffer */ + req->out.size += 1 + VWV(wct) + 2 + buflen; + + /* over allocate by a small amount */ + req->out.allocated = req->out.size + REQ_OVER_ALLOCATION; + + req->out.buffer = talloc_realloc(req->mem_ctx, req->out.buffer, req->out.allocated); + if (!req->out.buffer) { + exit_server(req->smb, "allocation failed"); + } + + req->out.hdr = req->out.buffer + NBT_HDR_SIZE; + req->out.vwv = req->out.buffer + chain_base_size + 1; + req->out.wct = wct; + req->out.data = req->out.vwv + VWV(wct) + 2; + req->out.data_size = buflen; + req->out.ptr = req->out.data; + + SCVAL(req->out.buffer, chain_base_size, wct); + SSVAL(req->out.vwv, VWV(wct), buflen); +} + + +/* + setup a reply in req->out with the given word count and initial data buffer size. + the caller will then fill in the command words and data before calling req_send_reply() to + send the reply on its way +*/ +void req_setup_reply(struct request_context *req, unsigned wct, unsigned buflen) +{ + if (req->chain_count != 0) { + req_setup_chain_reply(req, wct, buflen); + return; + } + + req->out.size = NBT_HDR_SIZE + MIN_SMB_SIZE + wct*2 + buflen; + + /* over allocate by a small amount */ + req->out.allocated = req->out.size + REQ_OVER_ALLOCATION; + + req->out.buffer = talloc(req->mem_ctx, req->out.allocated); + if (!req->out.buffer) { + exit_server(req->smb, "allocation failed"); + } + + req->out.hdr = req->out.buffer + NBT_HDR_SIZE; + req->out.vwv = req->out.hdr + HDR_VWV; + req->out.wct = wct; + req->out.data = req->out.vwv + VWV(wct) + 2; + req->out.data_size = buflen; + req->out.ptr = req->out.data; + + SIVAL(req->out.hdr, HDR_RCLS, 0); + + SCVAL(req->out.hdr, HDR_WCT, wct); + SSVAL(req->out.vwv, VWV(wct), buflen); + + + memcpy(req->out.hdr, "\377SMB", 4); + SCVAL(req->out.hdr,HDR_FLG, FLAG_REPLY | FLAG_CASELESS_PATHNAMES); + SSVAL(req->out.hdr,HDR_FLG2, + (req->flags2 & FLAGS2_UNICODE_STRINGS) | + FLAGS2_LONG_PATH_COMPONENTS | FLAGS2_32_BIT_ERROR_CODES | FLAGS2_EXTENDED_SECURITY); + + SSVAL(req->out.hdr,HDR_PIDHIGH,0); + memset(req->out.hdr + HDR_SS_FIELD, 0, 10); + + if (req->in.hdr) { + /* copy the cmd, tid, pid, uid and mid from the request */ + SCVAL(req->out.hdr,HDR_COM,CVAL(req->in.hdr,HDR_COM)); + SSVAL(req->out.hdr,HDR_TID,SVAL(req->in.hdr,HDR_TID)); + SSVAL(req->out.hdr,HDR_PID,SVAL(req->in.hdr,HDR_PID)); + SSVAL(req->out.hdr,HDR_UID,SVAL(req->in.hdr,HDR_UID)); + SSVAL(req->out.hdr,HDR_MID,SVAL(req->in.hdr,HDR_MID)); + } else { + SSVAL(req->out.hdr,HDR_TID,0); + SSVAL(req->out.hdr,HDR_PID,0); + SSVAL(req->out.hdr,HDR_UID,0); + SSVAL(req->out.hdr,HDR_MID,0); + } +} + +/* + work out the maximum data size we will allow for this reply, given + the negotiated max_xmit. The basic reply packet must be setup before + this call + + note that this is deliberately a signed integer reply +*/ +int req_max_data(struct request_context *req) +{ + int ret; + ret = req->smb->negotiate.max_send; + ret -= PTR_DIFF(req->out.data, req->out.hdr); + if (ret < 0) ret = 0; + return ret; +} + + +/* + grow the allocation of the data buffer portion of a reply + packet. Note that as this can reallocate the packet buffer this + invalidates any local pointers into the packet. + + To cope with this req->out.ptr is supplied. This will be updated to + point at the same offset into the packet as before this call +*/ +static void req_grow_allocation(struct request_context *req, unsigned new_size) +{ + int delta; + char *buf2; + + delta = new_size - req->out.data_size; + if (delta + req->out.size <= req->out.allocated) { + /* it fits in the preallocation */ + return; + } + + /* we need to realloc */ + req->out.allocated = req->out.size + delta + REQ_OVER_ALLOCATION; + buf2 = talloc_realloc(req->mem_ctx, req->out.buffer, req->out.allocated); + if (buf2 == NULL) { + smb_panic("out of memory in req_grow_allocation"); + } + + if (buf2 == req->out.buffer) { + /* the malloc library gave us the same pointer */ + return; + } + + /* update the pointers into the packet */ + req->out.data = buf2 + PTR_DIFF(req->out.data, req->out.buffer); + req->out.ptr = buf2 + PTR_DIFF(req->out.ptr, req->out.buffer); + req->out.vwv = buf2 + PTR_DIFF(req->out.vwv, req->out.buffer); + req->out.hdr = buf2 + PTR_DIFF(req->out.hdr, req->out.buffer); + + req->out.buffer = buf2; +} + + +/* + grow the data buffer portion of a reply packet. Note that as this + can reallocate the packet buffer this invalidates any local pointers + into the packet. + + To cope with this req->out.ptr is supplied. This will be updated to + point at the same offset into the packet as before this call +*/ +void req_grow_data(struct request_context *req, unsigned new_size) +{ + int delta; + + if (!(req->control_flags & REQ_CONTROL_LARGE) && new_size > req_max_data(req)) { + smb_panic("reply buffer too large!"); + } + + req_grow_allocation(req, new_size); + + delta = new_size - req->out.data_size; + + req->out.size += delta; + req->out.data_size += delta; + + /* set the BCC to the new data size */ + SSVAL(req->out.vwv, VWV(req->out.wct), new_size); +} + +/* + send a reply and destroy the request buffer + + note that this only looks at req->out.buffer and req->out.size, allowing manually + constructed packets to be sent +*/ +void req_send_reply(struct request_context *req) +{ + if (req->out.size > NBT_HDR_SIZE) { + _smb_setlen(req->out.buffer, req->out.size - NBT_HDR_SIZE); + } + + if (write_data(req->smb->socket.fd, req->out.buffer, req->out.size) != req->out.size) { + smb_panic("failed to send reply\n"); + } + + req_destroy(req); +} + + + +/* + construct and send an error packet with a forced DOS error code + this is needed to match win2000 behaviour for some parts of the protocol +*/ +void req_reply_dos_error(struct request_context *req, uint8 eclass, uint16 ecode) +{ + /* if the basic packet hasn't been setup yet then do it now */ + if (req->out.buffer == NULL) { + req_setup_reply(req, 0, 0); + } + + SCVAL(req->out.hdr, HDR_RCLS, eclass); + SSVAL(req->out.hdr, HDR_ERR, ecode); + + SSVAL(req->out.hdr, HDR_FLG2, SVAL(req->out.hdr, HDR_FLG2) & ~FLAGS2_32_BIT_ERROR_CODES); + + req_send_reply(req); +} + +/* + construct and send an error packet, then destroy the request + auto-converts to DOS error format when appropriate +*/ +void req_reply_error(struct request_context *req, NTSTATUS status) +{ + req_setup_reply(req, 0, 0); + + /* error returns never have any data */ + req_grow_data(req, 0); + + if (!lp_nt_status_support() || !(req->smb->negotiate.client_caps & CAP_STATUS32)) { + /* convert to DOS error codes */ + uint8 eclass; + uint32 ecode; + ntstatus_to_dos(status, &eclass, &ecode); + req_reply_dos_error(req, eclass, ecode); + return; + } + + SIVAL(req->out.hdr, HDR_RCLS, NT_STATUS_V(status)); + SSVAL(req->out.hdr, HDR_FLG2, SVAL(req->out.hdr, HDR_FLG2) | FLAGS2_32_BIT_ERROR_CODES); + + req_send_reply(req); +} + + +/* + push a string into the data portion of the request packet, growing it if necessary + this gets quite tricky - please be very careful to cover all cases when modifying this + + if dest is NULL, then put the string at the end of the data portion of the packet + + if dest_len is -1 then no limit applies +*/ +size_t req_push_str(struct request_context *req, char *dest, const char *str, int dest_len, unsigned flags) +{ + size_t len; + unsigned grow_size; + char *buf0; + const int max_bytes_per_char = 3; + + if (!(flags & (STR_ASCII|STR_UNICODE))) { + flags |= (req->smb->negotiate.client_caps & CAP_UNICODE) ? STR_UNICODE : STR_ASCII; + } + + if (dest == NULL) { + dest = req->out.data + req->out.data_size; + } + + if (dest_len != -1) { + len = dest_len; + } else { + len = (strlen(str)+2) * max_bytes_per_char; + } + + grow_size = len + PTR_DIFF(dest, req->out.data); + buf0 = req->out.buffer; + + req_grow_allocation(req, grow_size); + + if (buf0 != req->out.buffer) { + dest = req->out.buffer + PTR_DIFF(dest, buf0); + } + + len = push_string(req->out.hdr, dest, str, len, flags); + + grow_size = len + PTR_DIFF(dest, req->out.data); + + if (grow_size > req->out.data_size) { + req_grow_data(req, grow_size); + } + + return len; +} + + +/* + pull a UCS2 string from a request packet, returning a talloced unix string + + the string length is limited by the 3 things: + - the data size in the request (end of packet) + - the passed 'byte_len' if it is not -1 + - the end of string (null termination) + + Note that 'byte_len' is the number of bytes in the packet + + on failure zero is returned and *dest is set to NULL, otherwise the number + of bytes consumed in the packet is returned +*/ +static size_t req_pull_ucs2(struct request_context *req, const char **dest, const char *src, int byte_len, unsigned flags) +{ + int src_len, src_len2, alignment=0; + ssize_t ret; + + if (!(flags & STR_NOALIGN) && ucs2_align(req->in.buffer, src, flags)) { + src++; + alignment=1; + if (byte_len != -1) { + byte_len--; + } + } + + if (flags & STR_NO_RANGE_CHECK) { + src_len = byte_len; + } else { + src_len = req->in.data_size - PTR_DIFF(src, req->in.data); + if (src_len < 0) { + *dest = NULL; + return 0; + } + + if (byte_len != -1 && src_len > byte_len) { + src_len = byte_len; + } + } + + src_len2 = strnlen_w((const smb_ucs2_t *)src, src_len/2) * 2; + + if (src_len2 <= src_len - 2) { + /* include the termination if we didn't reach the end of the packet */ + src_len2 += 2; + } + + ret = convert_string_talloc(req->mem_ctx, CH_UCS2, CH_UNIX, src, src_len2, (const void **)dest); + + if (ret == -1) { + *dest = NULL; + return 0; + } + + return src_len2 + alignment; +} + +/* + pull a ascii string from a request packet, returning a talloced string + + the string length is limited by the 3 things: + - the data size in the request (end of packet) + - the passed 'byte_len' if it is not -1 + - the end of string (null termination) + + Note that 'byte_len' is the number of bytes in the packet + + on failure zero is returned and *dest is set to NULL, otherwise the number + of bytes consumed in the packet is returned +*/ +static size_t req_pull_ascii(struct request_context *req, const char **dest, const char *src, int byte_len, unsigned flags) +{ + int src_len, src_len2; + ssize_t ret; + + if (flags & STR_NO_RANGE_CHECK) { + src_len = byte_len; + } else { + src_len = req->in.data_size - PTR_DIFF(src, req->in.data); + if (src_len < 0) { + *dest = NULL; + return 0; + } + if (byte_len != -1 && src_len > byte_len) { + src_len = byte_len; + } + } + + src_len2 = strnlen(src, src_len); + if (src_len2 <= src_len - 1) { + /* include the termination if we didn't reach the end of the packet */ + src_len2++; + } + + ret = convert_string_talloc(req->mem_ctx, CH_DOS, CH_UNIX, src, src_len2, (const void **)dest); + + if (ret == -1) { + *dest = NULL; + return 0; + } + + return src_len2; +} + +/* + pull a string from a request packet, returning a talloced string + + the string length is limited by the 3 things: + - the data size in the request (end of packet) + - the passed 'byte_len' if it is not -1 + - the end of string (null termination) + + Note that 'byte_len' is the number of bytes in the packet + + on failure zero is returned and *dest is set to NULL, otherwise the number + of bytes consumed in the packet is returned +*/ +size_t req_pull_string(struct request_context *req, const char **dest, const char *src, int byte_len, unsigned flags) +{ + if (!(flags & STR_ASCII) && + ((flags & STR_UNICODE || (req->flags2 & FLAGS2_UNICODE_STRINGS)))) { + return req_pull_ucs2(req, dest, src, byte_len, flags); + } + + return req_pull_ascii(req, dest, src, byte_len, flags); +} + + +/* + pull a ASCII4 string buffer from a request packet, returning a talloced string + + an ASCII4 buffer is a null terminated string that has a prefix + of the character 0x4. It tends to be used in older parts of the protocol. + + on failure *dest is set to the zero length string. This seems to + match win2000 behaviour +*/ +size_t req_pull_ascii4(struct request_context *req, const char **dest, const char *src, unsigned flags) +{ + ssize_t ret; + + if (PTR_DIFF(src, req->in.data) + 1 > req->in.data_size) { + /* win2000 treats this as the NULL string! */ + (*dest) = talloc_strdup(req->mem_ctx, ""); + return 0; + } + + /* this consumes the 0x4 byte. We don't check whether the byte + is actually 0x4 or not. This matches win2000 server + behaviour */ + src++; + + ret = req_pull_string(req, dest, src, -1, flags); + if (ret == -1) { + (*dest) = talloc_strdup(req->mem_ctx, ""); + return 1; + } + + return ret + 1; +} + +/* + pull a DATA_BLOB from a request packet, returning a talloced blob + + return False if any part is outside the data portion of the packet +*/ +BOOL req_pull_blob(struct request_context *req, const char *src, int len, DATA_BLOB *blob) +{ + if (len != 0 && req_data_oob(req, src, len)) { + return False; + } + + (*blob) = data_blob_talloc(req->mem_ctx, src, len); + + return True; +} + +/* check that a lump of data in a request is within the bounds of the data section of + the packet */ +BOOL req_data_oob(struct request_context *req, const char *ptr, uint32 count) +{ + if (count == 0) { + return False; + } + + /* be careful with wraparound! */ + if (ptr < req->in.data || + ptr >= req->in.data + req->in.data_size || + count > req->in.data_size || + ptr + count > req->in.data + req->in.data_size) { + return True; + } + return False; +} + + +/* + pull an open file handle from a packet, taking account of the chained_fnum +*/ +uint16 req_fnum(struct request_context *req, const char *base, unsigned offset) +{ + if (req->chained_fnum != -1) { + return req->chained_fnum; + } + return SVAL(base, offset); +} diff --git a/source4/smbd/rewrite.c b/source4/smbd/rewrite.c new file mode 100644 index 0000000000..c2f08e0236 --- /dev/null +++ b/source4/smbd/rewrite.c @@ -0,0 +1,82 @@ +#include "includes.h" + +/* + + this is a set of temporary stub functions used during the core smbd rewrite. + This file will need to go away before the rewrite is complete +*/ + +void mangle_reset_cache(void) +{} + +void reset_stat_cache(void) +{} + + +BOOL set_current_service(void *conn, BOOL x) +{ return True; } + +void change_to_root_user(void) +{} + +void load_printers(void) +{} + +void file_init(void) +{} + +void init_rpc_pipe_hnd(void) +{} + +BOOL init_oplocks(void) +{ return True; } + +BOOL init_change_notify(void) +{ return True; } + + +BOOL pcap_printername_ok(const char *service, char *foo) +{ return True; } + +void become_root(void) +{} + +void unbecome_root(void) +{} + +BOOL namecache_enable(void) +{ return True; } + +BOOL locking_init(int read_only) +{ return True; } + +BOOL share_info_db_init(void) +{ return True; } + +BOOL init_registry(void) +{ return True; } + +BOOL share_access_check(struct request_context *req, struct tcon_context *conn, int snum, uint32 desired_access) +{ return True; } + +BOOL init_names(void) +{ return True; } + +BOOL uid_to_sid(DOM_SID *sid, uid_t uid) +{ + *sid = *get_global_sam_sid(); + sid_append_rid(sid, uid*2); + return True; +} + +BOOL gid_to_sid(DOM_SID *sid, gid_t gid) +{ + *sid = *get_global_sam_sid(); + sid_append_rid(sid, gid*2 + 1); + return True; +} + + +BOOL become_user_permanently(uid_t uid, gid_t gid) +{ return True; } + diff --git a/source4/smbd/server.c b/source4/smbd/server.c new file mode 100644 index 0000000000..e33a13e75d --- /dev/null +++ b/source4/smbd/server.c @@ -0,0 +1,340 @@ +/* + Unix SMB/CIFS implementation. + Main SMB server routines + Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) Martin Pool 2002 + Copyright (C) Jelmer Vernooij 2002 + Copyright (C) James J Myers 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + + +/* + called on a fatal error that should cause this server to terminate +*/ +void exit_server(struct server_context *smb, const char *reason) +{ + smb->model_ops->terminate_connection(smb, reason); +} + + +/* + add a socket address to the list of events, one event per port +*/ +static void add_socket(struct event_context *events, + struct model_ops *model_ops, + struct in_addr *ifip) +{ + char *ports = lp_smb_ports(); + char *ptr, *tok; + const char *delim = ", "; + + for (tok=strtok_r(ports, delim, &ptr); + tok; + tok=strtok_r(NULL, delim, &ptr)) { + unsigned port = atoi(tok); + struct fd_event fde; + + if (port == 0) continue; + + fde.fd = open_socket_in(SOCK_STREAM, port, 0, ifip->s_addr, True); + if (fde.fd == -1) { + DEBUG(0,("Failed to open socket on %s:%u - %s\n", + inet_ntoa(*ifip), port, strerror(errno))); + continue; + } + + /* ready to listen */ + set_socket_options(fde.fd, "SO_KEEPALIVE"); + set_socket_options(fde.fd, lp_socket_options()); + + if (listen(fde.fd, 10) == -1) { + DEBUG(0,("Failed to listen on %s:%d - %s\n", + inet_ntoa(*ifip), port, strerror(errno))); + close(fde.fd); + continue; + } + + /* we are only interested in read events on the listen socket */ + fde.flags = EVENT_FD_READ; + fde.private = model_ops; + fde.handler = model_ops->accept_connection; + + event_add_fd(events, &fde); + } +} + +/**************************************************************************** + Open the socket communication. +****************************************************************************/ +static void open_sockets_smbd(struct event_context *events, + struct model_ops *model_ops) +{ + if (lp_interfaces() && lp_bind_interfaces_only()) { + int num_interfaces = iface_count(); + int i; + + /* We have been given an interfaces line, and been + told to only bind to those interfaces. Create a + socket per interface and bind to only these. + */ + for(i = 0; i < num_interfaces; i++) { + struct in_addr *ifip = iface_n_ip(i); + + if (ifip == NULL) { + DEBUG(0,("open_sockets_smbd: interface %d has NULL IP address !\n", i)); + continue; + } + + add_socket(events, model_ops, ifip); + } + } else { + TALLOC_CTX *mem_ctx = talloc_init("open_sockets_smbd"); + + struct in_addr *ifip = interpret_addr2(mem_ctx, lp_socket_address()); + /* Just bind to lp_socket_address() (usually 0.0.0.0) */ + if (!mem_ctx) { + smb_panic("No memory"); + } + add_socket(events, model_ops, ifip); + talloc_destroy(mem_ctx); + } +} + +/**************************************************************************** + Reload the services file. +**************************************************************************/ +BOOL reload_services(struct server_context *smb, 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); + + if (smb) { + lp_killunused(smb, conn_snum_used); + } + + ret = lp_load(dyn_CONFIGFILE, False, False, True); + + load_printers(); + + /* perhaps the config filename is now set */ + if (!test) + reload_services(smb, True); + + reopen_logs(); + + load_interfaces(); + + mangle_reset_cache(); + reset_stat_cache(); + + /* this forces service parameters to be flushed */ + set_current_service(NULL,True); + + return(ret); +} + +/**************************************************************************** + Initialise connect, service and file structs. +****************************************************************************/ +static BOOL init_structs(void) +{ + init_names(); + file_init(); + init_rpc_pipe_hnd(); + secrets_init(); + + /* we want to re-seed early to prevent time delays causing + client problems at a later date. (tridge) */ + generate_random_buffer(NULL, 0, False); + + return True; +} + + +/* + setup the events for the chosen process model +*/ +static void setup_process_model(struct event_context *events, + const char *model) +{ + struct model_ops *ops; + + process_model_init(); + + ops = process_model_byname(model); + if (!ops) { + DEBUG(0,("Unknown process model '%s'\n", model)); + exit(-1); + } + + ops->model_startup(); + + /* now setup the listening sockets, adding + event handlers to the events structure */ + open_sockets_smbd(events, ops); +} + +/**************************************************************************** + main program. +****************************************************************************/ + int main(int argc,const char *argv[]) +{ + BOOL is_daemon = False; + BOOL interactive = False; + BOOL Fork = True; + BOOL log_stdout = False; + int opt; + poptContext pc; + struct event_context *events; + const char *model = "standard"; + 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, NULL, 0, "Listen on the specified ports"}, + {"model", 'M', POPT_ARG_STRING, &model, 0, "select process model"}, + POPT_COMMON_SAMBA + { NULL } + }; + + pc = poptGetContext("smbd", argc, argv, long_options, 0); + + while((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case 'b': + /* Display output to screen as well as debug */ + build_options(True); + exit(0); + break; + case 'p': + lp_set_cmdline("smb ports", poptGetOptArg(pc)); + break; + } + } + poptFreeContext(pc); + + events = event_context_init(); + + load_case_tables(); + + 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); + + fault_setup((void (*)(void *))exit_server); + + /* 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); + + reopen_logs(); + + DEBUG(0,("smbd version %s started.\n", SAMBA_VERSION)); + DEBUGADD(0,("Copyright Andrew Tridgell and the Samba Team 1992-2003\n")); + + /* 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); + } + DEBUG(0,("Using %s process model\n", model)); + + if (!reload_services(NULL, False)) + return(-1); + + init_structs(); + + 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 (!directory_exist(lp_lockdir(), NULL)) { + mkdir(lp_lockdir(), 0755); + } + + if (is_daemon) { + pidfile_create("smbd"); + } + + register_msg_pool_usage(); + register_dmalloc_msgs(); + + setup_process_model(events, model); + + /* wait for events */ + return event_loop_wait(events); +} diff --git a/source4/smbd/service.c b/source4/smbd/service.c new file mode 100644 index 0000000000..d219e6cee0 --- /dev/null +++ b/source4/smbd/service.c @@ -0,0 +1,339 @@ +/* + Unix SMB/CIFS implementation. + service (connection) handling + Copyright (C) Andrew Tridgell 1992-2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 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. service is always in dos codepage. + * + * @param service is modified (to canonical form??) + **/ +static int find_service(const char *service) +{ + int iService; + + iService = lp_servicenumber(service); + + /* now handle the special case of a home directory */ + if (iService == -1) { + 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. + */ +/* REWRITE: + 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 == -1) { + 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 == -1) { + } + + /* just possibly it's a default service? */ + if (iService == -1) { + char *pdefservice = lp_defaultservice(); + if (pdefservice && *pdefservice && + !strequal(pdefservice,service) && + !strstr(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 . + */ + pstring defservice; + pstrcpy(defservice, pdefservice); + iService = find_service(defservice); + if (iService >= 0) { + /* REWRITE: all_string_sub(service, "_","/",0); */ + iService = lp_add_service(service, iService); + } + } + } + + if (iService >= 0 && !VALID_SNUM(iService)) { + DEBUG(0,("Invalid snum %d for %s\n",iService, service)); + iService = -1; + } + + if (iService == -1) { + DEBUG(3,("find_service() failed to find service %s\n", service)); + } + + return iService; +} + + +/**************************************************************************** + Make a connection, given the snum to connect to, and the vuser of the + connecting user if appropriate. +****************************************************************************/ +static NTSTATUS make_connection_snum(struct request_context *req, + int snum, enum ntvfs_type type, + DATA_BLOB password, + const char *dev) +{ + struct tcon_context *conn; + NTSTATUS status; + + conn = conn_new(req->smb); + if (!conn) { + DEBUG(0,("Couldn't find free connection.\n")); + return NT_STATUS_INSUFFICIENT_RESOURCES; + } + req->conn = conn; + + conn->service = snum; + conn->type = type; + + /* + * 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. + * + */ + + if (!share_access_check(req, conn, snum, SA_RIGHT_FILE_WRITE_DATA)) { + if (!share_access_check(req, conn, snum, SA_RIGHT_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(req->smb, conn); + return NT_STATUS_ACCESS_DENIED; + } else { + conn->read_only = True; + } + } + + /* 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(req->smb, conn); + return NT_STATUS_INSUFFICIENT_RESOURCES; + } + + /* init ntvfs function pointers */ + status = ntvfs_init_connection(req); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("ntvfs_init_connection failed for service %s\n", lp_servicename(SNUM(conn)))); + conn_free(req->smb, conn); + return status; + } + + /* Invoke NTVFS connection hook */ + if (conn->ntvfs_ops->connect) { + status = conn->ntvfs_ops->connect(req, lp_servicename(snum)); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("make_connection: NTVFS make connection failed!\n")); + conn_free(req->smb, conn); + return status; + } + } + + return NT_STATUS_OK; +} + +/**************************************************************************** + Make a connection to a service. + * + * @param service +****************************************************************************/ +static NTSTATUS make_connection(struct request_context *req, + const char *service, DATA_BLOB password, + const char *dev, uint16 vuid) +{ + int snum; + enum ntvfs_type type; + const char *type_str; + + /* the service might be of the form \\SERVER\SHARE. Should we put + the server name we get from this somewhere? */ + if (strncmp(service, "\\\\", 2) == 0) { + char *p = strchr(service+2, '\\'); + if (p) { + service = p + 1; + } + } + + snum = find_service(service); + + if (snum == -1) { + DEBUG(0,("%s couldn't find service %s\n", + sub_get_remote_machine(), service)); + return NT_STATUS_BAD_NETWORK_NAME; + } + + /* work out what sort of connection this is */ + if (strcmp(lp_fstype(snum), "IPC") == 0) { + type = NTVFS_IPC; + type_str = "IPC"; + } else if (lp_print_ok(snum)) { + type = NTVFS_PRINT; + type_str = "LPT:"; + } else { + type = NTVFS_DISK; + type_str = "A:"; + } + + if (strcmp(dev, "?????") != 0 && strcasecmp(type_str, dev) != 0) { + /* the client gave us the wrong device type */ + return NT_STATUS_BAD_DEVICE_TYPE; + } + + return make_connection_snum(req, snum, type, password, dev); +} + +/**************************************************************************** +close a cnum +****************************************************************************/ +void close_cnum(struct tcon_context *conn) +{ + DEBUG(3, ("%s (%s) closed connection to service %s\n", + sub_get_remote_machine(),conn->smb->socket.client_addr, + lp_servicename(SNUM(conn)))); + + yield_connection(conn, lp_servicename(SNUM(conn))); + + /* tell the ntvfs backend that we are disconnecting */ + conn->ntvfs_ops->disconnect(conn); + + conn_free(conn->smb, conn); +} + + + +/* + backend for tree connect call +*/ +NTSTATUS tcon_backend(struct request_context *req, union smb_tcon *con) +{ + NTSTATUS status; + + /* can only do bare tcon in share level security */ + if (req->user_ctx == NULL && lp_security() != SEC_SHARE) { + return NT_STATUS_ACCESS_DENIED; + } + + if (con->generic.level == RAW_TCON_TCON) { + DATA_BLOB password; + password = data_blob(con->tcon.in.password, strlen(con->tcon.in.password) + 1); + + status = make_connection(req, con->tcon.in.service, password, con->tcon.in.dev, req->user_ctx->vuid); + + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + con->tcon.out.max_xmit = req->smb->negotiate.max_recv; + con->tcon.out.cnum = req->conn->cnum; + + return status; + } + + status = make_connection(req, con->tconx.in.path, con->tconx.in.password, + con->tconx.in.device, req->user_ctx->vuid); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + con->tconx.out.cnum = req->conn->cnum; + con->tconx.out.dev_type = talloc_strdup(req->mem_ctx, req->conn->dev_type); + con->tconx.out.fs_type = talloc_strdup(req->mem_ctx, req->conn->fs_type); + con->tconx.out.options = SMB_SUPPORT_SEARCH_BITS | (lp_csc_policy(req->conn->service) << 2); + if (lp_msdfs_root(req->conn->service) && lp_host_msdfs()) { + con->tconx.out.options |= SMB_SHARE_IN_DFS; + } + + return status; +} diff --git a/source4/smbd/session.c b/source4/smbd/session.c new file mode 100644 index 0000000000..7f85fca2ac --- /dev/null +++ b/source4/smbd/session.c @@ -0,0 +1,42 @@ +/* + 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" + +/* called when a session is created */ +BOOL session_claim(struct server_context *smb, user_struct *vuser) +{ + DEBUG(0,("rewrite: Not doing session claim\n")); + return True; +} + +/* called when a session is destroyed */ +void session_yield(user_struct *vuser) +{ + DEBUG(0,("rewrite: Not doing session yield\n")); +} + diff --git a/source4/smbd/sesssetup.c b/source4/smbd/sesssetup.c new file mode 100644 index 0000000000..c1ea446c3c --- /dev/null +++ b/source4/smbd/sesssetup.c @@ -0,0 +1,149 @@ +/* + Unix SMB/CIFS implementation. + handle SMBsessionsetup + Copyright (C) Andrew Tridgell 1998-2001 + Copyright (C) Andrew Bartlett 2001 + Copyright (C) Jim McDonough 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" + +/* + setup the OS, Lanman and domain portions of a session setup reply +*/ +static void sesssetup_common_strings(struct request_context *req, + char **os, char **lanman, char **domain) +{ + (*os) = talloc_asprintf(req->mem_ctx, "Unix"); + (*lanman) = talloc_asprintf(req->mem_ctx, "Samba %s", SAMBA_VERSION); + (*domain) = talloc_asprintf(req->mem_ctx, "%s", lp_workgroup()); +} + + +/* + handler for old style session setup +*/ +static NTSTATUS sesssetup_old(struct request_context *req, union smb_sesssetup *sess) +{ + NTSTATUS status; + auth_usersupplied_info *user_info = NULL; + auth_serversupplied_info *server_info = NULL; + DATA_BLOB null_blob; + + if (!req->smb->negotiate.done_sesssetup) { + req->smb->negotiate.max_send = sess->old.in.bufsize; + } + + null_blob.length = 0; + + status = make_user_info_for_reply_enc(&user_info, + sess->old.in.user, sess->old.in.domain, + sess->old.in.password, + null_blob); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_ACCESS_DENIED; + } + + status = req->smb->negotiate.auth_context->check_ntlm_password(req->smb->negotiate.auth_context, + user_info, + &server_info); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_ACCESS_DENIED; + } + + sess->old.out.action = 0; + sess->old.out.vuid = register_vuid(req->smb, server_info, sess->old.in.user); + sesssetup_common_strings(req, + &sess->old.out.os, + &sess->old.out.lanman, + &sess->old.out.domain); + + return NT_STATUS_OK; +} + + +/* + handler for NT1 style session setup +*/ +static NTSTATUS sesssetup_nt1(struct request_context *req, union smb_sesssetup *sess) +{ + NTSTATUS status; + auth_usersupplied_info *user_info = NULL; + auth_serversupplied_info *server_info = NULL; + + if (!req->smb->negotiate.done_sesssetup) { + req->smb->negotiate.max_send = sess->nt1.in.bufsize; + req->smb->negotiate.client_caps = sess->nt1.in.capabilities; + } + + status = make_user_info_for_reply_enc(&user_info, + sess->nt1.in.user, sess->nt1.in.domain, + sess->nt1.in.password1, + sess->nt1.in.password2); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_ACCESS_DENIED; + } + + status = req->smb->negotiate.auth_context->check_ntlm_password(req->smb->negotiate.auth_context, + user_info, + &server_info); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_ACCESS_DENIED; + } + + sess->nt1.out.action = 0; + sess->nt1.out.vuid = register_vuid(req->smb, server_info, sess->old.in.user); + sesssetup_common_strings(req, + &sess->nt1.out.os, + &sess->nt1.out.lanman, + &sess->nt1.out.domain); + + return NT_STATUS_OK; +} + + +/* + handler for SPNEGO style session setup +*/ +static NTSTATUS sesssetup_spnego(struct request_context *req, union smb_sesssetup *sess) +{ + /* defer this one for now */ + return NT_STATUS_INVALID_LEVEL; +} + +/* + backend for sessionsetup call - this takes all 3 varients of the call +*/ +NTSTATUS sesssetup_backend(struct request_context *req, + union smb_sesssetup *sess) +{ + switch (sess->generic.level) { + case RAW_SESSSETUP_OLD: + return sesssetup_old(req, sess); + case RAW_SESSSETUP_NT1: + return sesssetup_nt1(req, sess); + case RAW_SESSSETUP_SPNEGO: + return sesssetup_spnego(req, sess); + } + + req->smb->negotiate.done_sesssetup = True; + + return NT_STATUS_INVALID_LEVEL; +} + + diff --git a/source4/smbd/tcon.c b/source4/smbd/tcon.c new file mode 100644 index 0000000000..b28b04f643 --- /dev/null +++ b/source4/smbd/tcon.c @@ -0,0 +1,3 @@ + + + diff --git a/source4/smbd/trans2.c b/source4/smbd/trans2.c new file mode 100644 index 0000000000..b26dbd505a --- /dev/null +++ b/source4/smbd/trans2.c @@ -0,0 +1,1343 @@ +/* + Unix SMB/CIFS implementation. + transaction2 handling + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the 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 parsing of transact2 requests +*/ + +#include "includes.h" + + +#define CHECK_MIN_BLOB_SIZE(blob, size) do { \ + if ((blob)->length < (size)) { \ + return NT_STATUS_INFO_LENGTH_MISMATCH; \ + }} while (0) + +/* grow the data allocation size of a trans2 reply - this guarantees + that requests to grow the data size later will not change the + pointer */ +static void trans2_grow_data_allocation(struct request_context *req, + struct smb_trans2 *trans, + uint16 new_size) +{ + if (new_size <= trans->out.data.length) { + return; + } + trans->out.data.data = talloc_realloc(req->mem_ctx, trans->out.data.data, new_size); +} + + +/* grow the data size of a trans2 reply */ +static void trans2_grow_data(struct request_context *req, + struct smb_trans2 *trans, + uint16 new_size) +{ + trans2_grow_data_allocation(req, trans, new_size); + trans->out.data.length = new_size; +} + +/* grow the data, zero filling any new bytes */ +static void trans2_grow_data_fill(struct request_context *req, + struct smb_trans2 *trans, + uint16 new_size) +{ + uint16 old_size = trans->out.data.length; + trans2_grow_data(req, trans, new_size); + if (new_size > old_size) { + memset(trans->out.data.data + old_size, 0, new_size - old_size); + } +} + + +/* setup a trans2 reply, given the data and params sizes */ +static void trans2_setup_reply(struct request_context *req, + struct smb_trans2 *trans, + uint16 param_size, uint16 data_size, + uint16 setup_count) +{ + trans->out.setup_count = setup_count; + if (setup_count != 0) { + trans->out.setup = talloc_zero(req->mem_ctx, sizeof(uint16) * setup_count); + } + trans->out.params = data_blob_talloc(req->mem_ctx, NULL, param_size); + trans->out.data = data_blob_talloc(req->mem_ctx, NULL, data_size); +} + + +/* + pull a string from a blob in a trans2 request +*/ +static size_t trans2_pull_blob_string(struct request_context *req, + const DATA_BLOB *blob, + uint16 offset, + const char **str, + int flags) +{ + /* we use STR_NO_RANGE_CHECK because the params are allocated + separately in a DATA_BLOB, so we need to do our own range + checking */ + if (offset >= blob->length) { + *str = NULL; + return 0; + } + + return req_pull_string(req, str, + blob->data + offset, + blob->length - offset, + STR_NO_RANGE_CHECK | flags); +} + +/* + push a string into the data section of a trans2 request + return the number of bytes consumed in the output +*/ +static size_t trans2_push_data_string(struct request_context *req, + struct smb_trans2 *trans, + uint16 len_offset, + uint16 offset, + const WIRE_STRING *str, + int dest_len, + int flags) +{ + int alignment = 0, ret = 0, pkt_len; + + /* we use STR_NO_RANGE_CHECK because the params are allocated + separately in a DATA_BLOB, so we need to do our own range + checking */ + if (!str->s || offset >= trans->out.data.length) { + if (flags & STR_LEN8BIT) { + SCVAL(trans->out.data.data, len_offset, 0); + } else { + SIVAL(trans->out.data.data, len_offset, 0); + } + return 0; + } + + flags |= STR_NO_RANGE_CHECK; + + if (dest_len == -1 || (dest_len > trans->out.data.length - offset)) { + dest_len = trans->out.data.length - offset; + } + + if (!(flags & (STR_ASCII|STR_UNICODE))) { + flags |= (req->smb->negotiate.client_caps & CAP_UNICODE) ? STR_UNICODE : STR_ASCII; + } + + if ((offset&1) && (flags & STR_UNICODE) && !(flags & STR_NOALIGN)) { + alignment = 1; + if (dest_len > 0) { + SCVAL(trans->out.data.data + offset, 0, 0); + ret = push_string(NULL, trans->out.data.data + offset + 1, str->s, dest_len-1, flags); + } + } else { + ret = push_string(NULL, trans->out.data.data + offset, str->s, dest_len, flags); + } + + /* sometimes the string needs to be terminated, but the length + on the wire must not include the termination! */ + pkt_len = ret; + + if ((flags & STR_LEN_NOTERM) && (flags & STR_TERMINATE)) { + if ((flags & STR_UNICODE) && ret >= 2) { + pkt_len = ret-2; + } + if ((flags & STR_ASCII) && ret >= 1) { + pkt_len = ret-1; + } + } + + if (flags & STR_LEN8BIT) { + SCVAL(trans->out.data.data, len_offset, pkt_len); + } else { + SIVAL(trans->out.data.data, len_offset, pkt_len); + } + + return ret + alignment; +} + +/* + append a string to the data section of a trans2 reply + len_offset points to the place in the packet where the length field + should go +*/ +static void trans2_append_data_string(struct request_context *req, + struct smb_trans2 *trans, + const WIRE_STRING *str, + uint_t len_offset, + int flags) +{ + size_t ret; + uint16 offset; + const int max_bytes_per_char = 3; + + offset = trans->out.data.length; + trans2_grow_data(req, trans, offset + (2+strlen(str->s))*max_bytes_per_char); + ret = trans2_push_data_string(req, trans, len_offset, offset, str, -1, flags); + trans2_grow_data(req, trans, offset + ret); +} + + +/* + trans2 qfsinfo implementation +*/ +static NTSTATUS trans2_qfsinfo(struct request_context *req, struct smb_trans2 *trans) +{ + union smb_fsinfo fsinfo; + NTSTATUS status; + uint16 level; + uint_t i; + + /* make sure we got enough parameters */ + if (trans->in.params.length != 2) { + return NT_STATUS_FOOBAR; + } + + level = SVAL(trans->in.params.data, 0); + + switch (level) { + case SMB_QFS_ALLOCATION: + fsinfo.allocation.level = RAW_QFS_ALLOCATION; + + status = req->conn->ntvfs_ops->fsinfo(req, &fsinfo); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + trans2_setup_reply(req, trans, 0, 18, 0); + + SIVAL(trans->out.data.data, 0, fsinfo.allocation.out.fs_id); + SIVAL(trans->out.data.data, 4, fsinfo.allocation.out.sectors_per_unit); + SIVAL(trans->out.data.data, 8, fsinfo.allocation.out.total_alloc_units); + SIVAL(trans->out.data.data, 12, fsinfo.allocation.out.avail_alloc_units); + SSVAL(trans->out.data.data, 16, fsinfo.allocation.out.bytes_per_sector); + + return NT_STATUS_OK; + + case SMB_QFS_VOLUME: + fsinfo.volume.level = RAW_QFS_VOLUME; + + status = req->conn->ntvfs_ops->fsinfo(req, &fsinfo); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + trans2_setup_reply(req, trans, 0, 5, 0); + + SIVAL(trans->out.data.data, 0, fsinfo.volume.out.serial_number); + /* w2k3 implements this incorrectly for unicode - it + * leaves the last byte off the string */ + trans2_append_data_string(req, trans, + &fsinfo.volume.out.volume_name, + 4, STR_LEN8BIT|STR_NOALIGN); + + return NT_STATUS_OK; + + case SMB_QFS_VOLUME_INFO: + case SMB_QFS_VOLUME_INFORMATION: + fsinfo.volume_info.level = RAW_QFS_VOLUME_INFO; + + status = req->conn->ntvfs_ops->fsinfo(req, &fsinfo); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + trans2_setup_reply(req, trans, 0, 18, 0); + + push_nttime(trans->out.data.data, 0, &fsinfo.volume_info.out.create_time); + SIVAL(trans->out.data.data, 8, fsinfo.volume_info.out.serial_number); + trans2_append_data_string(req, trans, + &fsinfo.volume_info.out.volume_name, + 12, STR_UNICODE); + + return NT_STATUS_OK; + + case SMB_QFS_SIZE_INFO: + case SMB_QFS_SIZE_INFORMATION: + fsinfo.size_info.level = RAW_QFS_SIZE_INFO; + + status = req->conn->ntvfs_ops->fsinfo(req, &fsinfo); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + trans2_setup_reply(req, trans, 0, 24, 0); + + SBVAL(trans->out.data.data, 0, fsinfo.size_info.out.total_alloc_units); + SBVAL(trans->out.data.data, 8, fsinfo.size_info.out.avail_alloc_units); + SIVAL(trans->out.data.data, 16, fsinfo.size_info.out.sectors_per_unit); + SIVAL(trans->out.data.data, 20, fsinfo.size_info.out.bytes_per_sector); + + return NT_STATUS_OK; + + case SMB_QFS_DEVICE_INFO: + case SMB_QFS_DEVICE_INFORMATION: + fsinfo.device_info.level = RAW_QFS_DEVICE_INFO; + + status = req->conn->ntvfs_ops->fsinfo(req, &fsinfo); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + trans2_setup_reply(req, trans, 0, 8, 0); + SIVAL(trans->out.data.data, 0, fsinfo.device_info.out.device_type); + SIVAL(trans->out.data.data, 4, fsinfo.device_info.out.characteristics); + return NT_STATUS_OK; + + + case SMB_QFS_ATTRIBUTE_INFO: + case SMB_QFS_ATTRIBUTE_INFORMATION: + fsinfo.attribute_info.level = RAW_QFS_ATTRIBUTE_INFO; + + status = req->conn->ntvfs_ops->fsinfo(req, &fsinfo); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + trans2_setup_reply(req, trans, 0, 12, 0); + + SIVAL(trans->out.data.data, 0, fsinfo.attribute_info.out.fs_attr); + SIVAL(trans->out.data.data, 4, fsinfo.attribute_info.out.max_file_component_length); + /* this must not be null terminated or win98 gets + confused! also note that w2k3 returns this as + unicode even when ascii is negotiated */ + trans2_append_data_string(req, trans, + &fsinfo.attribute_info.out.fs_type, + 8, STR_UNICODE); + return NT_STATUS_OK; + + + case SMB_QFS_QUOTA_INFORMATION: + fsinfo.quota_information.level = RAW_QFS_QUOTA_INFORMATION; + + status = req->conn->ntvfs_ops->fsinfo(req, &fsinfo); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + trans2_setup_reply(req, trans, 0, 48, 0); + + SBVAL(trans->out.data.data, 0, fsinfo.quota_information.out.unknown[0]); + SBVAL(trans->out.data.data, 8, fsinfo.quota_information.out.unknown[1]); + SBVAL(trans->out.data.data, 16, fsinfo.quota_information.out.unknown[2]); + SBVAL(trans->out.data.data, 24, fsinfo.quota_information.out.quota_soft); + SBVAL(trans->out.data.data, 32, fsinfo.quota_information.out.quota_hard); + SBVAL(trans->out.data.data, 40, fsinfo.quota_information.out.quota_flags); + + return NT_STATUS_OK; + + + case SMB_QFS_FULL_SIZE_INFORMATION: + fsinfo.full_size_information.level = RAW_QFS_FULL_SIZE_INFORMATION; + + status = req->conn->ntvfs_ops->fsinfo(req, &fsinfo); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + trans2_setup_reply(req, trans, 0, 32, 0); + + SBVAL(trans->out.data.data, 0, fsinfo.full_size_information.out.total_alloc_units); + SBVAL(trans->out.data.data, 8, fsinfo.full_size_information.out.call_avail_alloc_units); + SBVAL(trans->out.data.data, 16, fsinfo.full_size_information.out.actual_avail_alloc_units); + SIVAL(trans->out.data.data, 24, fsinfo.full_size_information.out.sectors_per_unit); + SIVAL(trans->out.data.data, 28, fsinfo.full_size_information.out.bytes_per_sector); + + return NT_STATUS_OK; + + case SMB_QFS_OBJECTID_INFORMATION: + fsinfo.objectid_information.level = RAW_QFS_OBJECTID_INFORMATION; + + status = req->conn->ntvfs_ops->fsinfo(req, &fsinfo); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + trans2_setup_reply(req, trans, 0, 64, 0); + + memcpy(trans->out.data.data, fsinfo.objectid_information.out.guid.info, GUID_SIZE); + for (i=0;i<6;i++) { + SBVAL(trans->out.data.data, 16 + 8*i, fsinfo.objectid_information.out.unknown[i]); + } + return NT_STATUS_OK; + } + + return NT_STATUS_INVALID_LEVEL; +} + +/* + fill in the reply from a qpathinfo or qfileinfo call +*/ +static NTSTATUS trans2_fileinfo_fill(struct request_context *req, struct smb_trans2 *trans, + union smb_fileinfo *st) +{ + uint_t i; + + switch (st->generic.level) { + case RAW_FILEINFO_GENERIC: + case RAW_FILEINFO_GETATTR: + case RAW_FILEINFO_GETATTRE: + /* handled elsewhere */ + return NT_STATUS_INVALID_LEVEL; + + case RAW_FILEINFO_BASIC_INFO: + case RAW_FILEINFO_BASIC_INFORMATION: + trans2_setup_reply(req, trans, 2, 40, 0); + + SSVAL(trans->out.params.data, 0, 0); + push_nttime(trans->out.data.data, 0, &st->basic_info.out.create_time); + push_nttime(trans->out.data.data, 8, &st->basic_info.out.access_time); + push_nttime(trans->out.data.data, 16, &st->basic_info.out.write_time); + push_nttime(trans->out.data.data, 24, &st->basic_info.out.change_time); + SIVAL(trans->out.data.data, 32, st->basic_info.out.attrib); + SIVAL(trans->out.data.data, 36, 0); /* padding */ + return NT_STATUS_OK; + + case RAW_FILEINFO_STANDARD: + trans2_setup_reply(req, trans, 2, 22, 0); + + SSVAL(trans->out.params.data, 0, 0); + put_dos_date2(trans->out.data.data, 0, st->standard.out.create_time); + put_dos_date2(trans->out.data.data, 4, st->standard.out.access_time); + put_dos_date2(trans->out.data.data, 8, st->standard.out.write_time); + SIVAL(trans->out.data.data, 12, st->standard.out.size); + SIVAL(trans->out.data.data, 16, st->standard.out.alloc_size); + SSVAL(trans->out.data.data, 20, st->standard.out.attrib); + return NT_STATUS_OK; + + case RAW_FILEINFO_EA_SIZE: + trans2_setup_reply(req, trans, 2, 26, 0); + + SSVAL(trans->out.params.data, 0, 0); + put_dos_date2(trans->out.data.data, 0, st->ea_size.out.create_time); + put_dos_date2(trans->out.data.data, 4, st->ea_size.out.access_time); + put_dos_date2(trans->out.data.data, 8, st->ea_size.out.write_time); + SIVAL(trans->out.data.data, 12, st->ea_size.out.size); + SIVAL(trans->out.data.data, 16, st->ea_size.out.alloc_size); + SSVAL(trans->out.data.data, 20, st->ea_size.out.attrib); + SIVAL(trans->out.data.data, 22, st->ea_size.out.ea_size); + return NT_STATUS_OK; + + case RAW_FILEINFO_NETWORK_OPEN_INFORMATION: + trans2_setup_reply(req, trans, 2, 56, 0); + + SSVAL(trans->out.params.data, 0, 0); + push_nttime(trans->out.data.data, 0, &st->network_open_information.out.create_time); + push_nttime(trans->out.data.data, 8, &st->network_open_information.out.access_time); + push_nttime(trans->out.data.data, 16, &st->network_open_information.out.write_time); + push_nttime(trans->out.data.data, 24, &st->network_open_information.out.change_time); + SBVAL(trans->out.data.data, 32, st->network_open_information.out.alloc_size); + SBVAL(trans->out.data.data, 40, st->network_open_information.out.size); + SIVAL(trans->out.data.data, 48, st->network_open_information.out.attrib); + SIVAL(trans->out.data.data, 52, 0); /* padding */ + return NT_STATUS_OK; + + case RAW_FILEINFO_STANDARD_INFO: + case RAW_FILEINFO_STANDARD_INFORMATION: + trans2_setup_reply(req, trans, 2, 24, 0); + SSVAL(trans->out.params.data, 0, 0); + SBVAL(trans->out.data.data, 0, st->standard_info.out.alloc_size); + SBVAL(trans->out.data.data, 8, st->standard_info.out.size); + SIVAL(trans->out.data.data, 16, st->standard_info.out.nlink); + SCVAL(trans->out.data.data, 20, st->standard_info.out.delete_pending); + SCVAL(trans->out.data.data, 21, st->standard_info.out.directory); + SSVAL(trans->out.data.data, 22, 0); /* padding */ + return NT_STATUS_OK; + + case RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION: + trans2_setup_reply(req, trans, 2, 8, 0); + SSVAL(trans->out.params.data, 0, 0); + SIVAL(trans->out.data.data, 0, st->attribute_tag_information.out.attrib); + SIVAL(trans->out.data.data, 4, st->attribute_tag_information.out.reparse_tag); + return NT_STATUS_OK; + + case RAW_FILEINFO_EA_INFO: + case RAW_FILEINFO_EA_INFORMATION: + trans2_setup_reply(req, trans, 2, 4, 0); + SSVAL(trans->out.params.data, 0, 0); + SIVAL(trans->out.data.data, 0, st->ea_info.out.ea_size); + return NT_STATUS_OK; + + case RAW_FILEINFO_MODE_INFORMATION: + trans2_setup_reply(req, trans, 2, 4, 0); + SSVAL(trans->out.params.data, 0, 0); + SIVAL(trans->out.data.data, 0, st->mode_information.out.mode); + return NT_STATUS_OK; + + case RAW_FILEINFO_ALIGNMENT_INFORMATION: + trans2_setup_reply(req, trans, 2, 4, 0); + SSVAL(trans->out.params.data, 0, 0); + SIVAL(trans->out.data.data, 0, + st->alignment_information.out.alignment_requirement); + return NT_STATUS_OK; + + case RAW_FILEINFO_ALL_EAS: + if (st->all_eas.out.num_eas == 0) { + trans2_setup_reply(req, trans, 2, 4, 0); + SSVAL(trans->out.params.data, 0, 0); + SIVAL(trans->out.data.data, 0, 0); + } else { + uint32 list_size = ea_list_size(st->all_eas.out.num_eas, + st->all_eas.out.eas); + trans2_setup_reply(req, trans, 2, list_size, 0); + SSVAL(trans->out.params.data, 0, 0); + ea_put_list(trans->out.data.data, + st->all_eas.out.num_eas, st->all_eas.out.eas); + } + return NT_STATUS_OK; + + case RAW_FILEINFO_ACCESS_INFORMATION: + trans2_setup_reply(req, trans, 2, 4, 0); + SSVAL(trans->out.params.data, 0, 0); + SIVAL(trans->out.data.data, 0, st->access_information.out.access_flags); + return NT_STATUS_OK; + + case RAW_FILEINFO_POSITION_INFORMATION: + trans2_setup_reply(req, trans, 2, 8, 0); + SSVAL(trans->out.params.data, 0, 0); + SBVAL(trans->out.data.data, 0, st->position_information.out.position); + return NT_STATUS_OK; + + case RAW_FILEINFO_COMPRESSION_INFO: + case RAW_FILEINFO_COMPRESSION_INFORMATION: + trans2_setup_reply(req, trans, 2, 16, 0); + SSVAL(trans->out.params.data, 0, 0); + SBVAL(trans->out.data.data, 0, st->compression_info.out.compressed_size); + SSVAL(trans->out.data.data, 8, st->compression_info.out.format); + SCVAL(trans->out.data.data, 10, st->compression_info.out.unit_shift); + SCVAL(trans->out.data.data, 11, st->compression_info.out.chunk_shift); + SCVAL(trans->out.data.data, 12, st->compression_info.out.cluster_shift); + SSVAL(trans->out.data.data, 13, 0); /* 3 bytes padding */ + SCVAL(trans->out.data.data, 15, 0); + return NT_STATUS_OK; + + case RAW_FILEINFO_IS_NAME_VALID: + trans2_setup_reply(req, trans, 2, 0, 0); + SSVAL(trans->out.params.data, 0, 0); + return NT_STATUS_OK; + + case RAW_FILEINFO_INTERNAL_INFORMATION: + trans2_setup_reply(req, trans, 2, 8, 0); + SSVAL(trans->out.params.data, 0, 0); + SIVAL(trans->out.data.data, 0, st->internal_information.out.device); + SIVAL(trans->out.data.data, 4, st->internal_information.out.inode); + return NT_STATUS_OK; + + case RAW_FILEINFO_ALL_INFO: + case RAW_FILEINFO_ALL_INFORMATION: + trans2_setup_reply(req, trans, 2, 72, 0); + + SSVAL(trans->out.params.data, 0, 0); + push_nttime(trans->out.data.data, 0, &st->all_info.out.create_time); + push_nttime(trans->out.data.data, 8, &st->all_info.out.access_time); + push_nttime(trans->out.data.data, 16, &st->all_info.out.write_time); + push_nttime(trans->out.data.data, 24, &st->all_info.out.change_time); + SIVAL(trans->out.data.data, 32, st->all_info.out.attrib); + SBVAL(trans->out.data.data, 40, st->all_info.out.alloc_size); + SBVAL(trans->out.data.data, 48, st->all_info.out.size); + SIVAL(trans->out.data.data, 56, st->all_info.out.nlink); + SCVAL(trans->out.data.data, 60, st->all_info.out.delete_pending); + SCVAL(trans->out.data.data, 61, st->all_info.out.directory); + SSVAL(trans->out.data.data, 62, 0); /* padding */ + SIVAL(trans->out.data.data, 64, st->all_info.out.ea_size); + trans2_append_data_string(req, trans, &st->all_info.out.fname, + 68, 0); + return NT_STATUS_OK; + + case RAW_FILEINFO_NAME_INFO: + case RAW_FILEINFO_NAME_INFORMATION: + trans2_setup_reply(req, trans, 2, 4, 0); + SSVAL(trans->out.params.data, 0, 0); + trans2_append_data_string(req, trans, &st->name_info.out.fname, 0, 0); + return NT_STATUS_OK; + + case RAW_FILEINFO_ALT_NAME_INFO: + case RAW_FILEINFO_ALT_NAME_INFORMATION: + trans2_setup_reply(req, trans, 2, 4, 0); + SSVAL(trans->out.params.data, 0, 0); + trans2_append_data_string(req, trans, &st->alt_name_info.out.fname, 0, 0); + return NT_STATUS_OK; + + case RAW_FILEINFO_STREAM_INFO: + case RAW_FILEINFO_STREAM_INFORMATION: + trans2_setup_reply(req, trans, 2, 0, 0); + + SSVAL(trans->out.params.data, 0, 0); + + for (i=0;istream_info.out.num_streams;i++) { + uint16 data_size = trans->out.data.length; + char *data; + + trans2_grow_data(req, trans, data_size + 24); + data = trans->out.data.data + data_size; + SBVAL(data, 8, st->stream_info.out.streams[i].size); + SBVAL(data, 16, st->stream_info.out.streams[i].alloc_size); + trans2_append_data_string(req, trans, + &st->stream_info.out.streams[i].stream_name, + data_size + 4, STR_UNICODE); + if (i == st->stream_info.out.num_streams - 1) { + SIVAL(trans->out.data.data, data_size, 0); + } else { + trans2_grow_data_fill(req, trans, (trans->out.data.length+7)&~7); + SIVAL(trans->out.data.data, data_size, + trans->out.data.length - data_size); + } + } + return NT_STATUS_OK; + } + + return NT_STATUS_INVALID_LEVEL; +} + +/* + trans2 qpathinfo implementation +*/ +static NTSTATUS trans2_qpathinfo(struct request_context *req, struct smb_trans2 *trans) +{ + union smb_fileinfo st; + NTSTATUS status; + uint16 level; + + /* make sure we got enough parameters */ + if (trans->in.params.length < 8) { + return NT_STATUS_FOOBAR; + } + + level = SVAL(trans->in.params.data, 0); + + trans2_pull_blob_string(req, &trans->in.params, 6, &st.generic.in.fname, 0); + if (st.generic.in.fname == NULL) { + return NT_STATUS_FOOBAR; + } + + /* work out the backend level - we make it 1-1 in the header */ + st.generic.level = (enum fileinfo_level)level; + if (st.generic.level >= RAW_FILEINFO_GENERIC) { + return NT_STATUS_INVALID_LEVEL; + } + + /* call the backend */ + status = req->conn->ntvfs_ops->qpathinfo(req, &st); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* fill in the reply parameters */ + status = trans2_fileinfo_fill(req, trans, &st); + + return status; +} + + +/* + trans2 qpathinfo implementation +*/ +static NTSTATUS trans2_qfileinfo(struct request_context *req, struct smb_trans2 *trans) +{ + union smb_fileinfo st; + NTSTATUS status; + uint16 level; + + /* make sure we got enough parameters */ + if (trans->in.params.length < 4) { + return NT_STATUS_FOOBAR; + } + + st.generic.in.fnum = SVAL(trans->in.params.data, 0); + level = SVAL(trans->in.params.data, 2); + + /* work out the backend level - we make it 1-1 in the header */ + st.generic.level = (enum fileinfo_level)level; + if (st.generic.level >= RAW_FILEINFO_GENERIC) { + return NT_STATUS_INVALID_LEVEL; + } + + /* call the backend */ + status = req->conn->ntvfs_ops->qfileinfo(req, &st); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* fill in the reply parameters */ + status = trans2_fileinfo_fill(req, trans, &st); + + return status; +} + + +/* + parse a trans2 setfileinfo/setpathinfo data blob +*/ +static NTSTATUS trans2_parse_sfileinfo(struct request_context *req, + union smb_setfileinfo *st, + const DATA_BLOB *blob) +{ + uint32 len; + + switch (st->generic.level) { + case RAW_SFILEINFO_GENERIC: + case RAW_SFILEINFO_SETATTR: + case RAW_SFILEINFO_SETATTRE: + /* handled elsewhere */ + return NT_STATUS_INVALID_LEVEL; + + case RAW_SFILEINFO_STANDARD: + CHECK_MIN_BLOB_SIZE(blob, 12); + st->standard.in.create_time = make_unix_date2(blob->data + 0); + st->standard.in.access_time = make_unix_date2(blob->data + 4); + st->standard.in.write_time = make_unix_date2(blob->data + 8); + return NT_STATUS_OK; + + case RAW_SFILEINFO_EA_SET: + CHECK_MIN_BLOB_SIZE(blob, 4); + len = IVAL(blob->data, 0); + if (len > blob->length || len < 4) { + return NT_STATUS_INFO_LENGTH_MISMATCH; + } + { + DATA_BLOB blob2; + blob2.data = blob->data+4; + blob2.length = len-4; + len = ea_pull_struct(&blob2, req->mem_ctx, &st->ea_set.in.ea); + } + if (len == 0) { + return NT_STATUS_INVALID_PARAMETER; + } + return NT_STATUS_OK; + + case SMB_SFILEINFO_BASIC_INFO: + case SMB_SFILEINFO_BASIC_INFORMATION: + CHECK_MIN_BLOB_SIZE(blob, 36); + st->basic_info.in.create_time = pull_nttime(blob->data, 0); + st->basic_info.in.access_time = pull_nttime(blob->data, 8); + st->basic_info.in.write_time = pull_nttime(blob->data, 16); + st->basic_info.in.change_time = pull_nttime(blob->data, 24); + st->basic_info.in.attrib = IVAL(blob->data, 32); + return NT_STATUS_OK; + + case SMB_SFILEINFO_DISPOSITION_INFO: + case SMB_SFILEINFO_DISPOSITION_INFORMATION: + CHECK_MIN_BLOB_SIZE(blob, 1); + st->disposition_info.in.delete_on_close = CVAL(blob->data, 0); + return NT_STATUS_OK; + + case SMB_SFILEINFO_ALLOCATION_INFO: + case SMB_SFILEINFO_ALLOCATION_INFORMATION: + CHECK_MIN_BLOB_SIZE(blob, 8); + st->allocation_info.in.alloc_size = BVAL(blob->data, 0); + return NT_STATUS_OK; + + case RAW_SFILEINFO_END_OF_FILE_INFO: + case RAW_SFILEINFO_END_OF_FILE_INFORMATION: + CHECK_MIN_BLOB_SIZE(blob, 8); + st->end_of_file_info.in.size = BVAL(blob->data, 0); + return NT_STATUS_OK; + + case RAW_SFILEINFO_RENAME_INFORMATION: { + DATA_BLOB blob2; + + CHECK_MIN_BLOB_SIZE(blob, 12); + st->rename_information.in.overwrite = CVAL(blob->data, 0); + st->rename_information.in.root_fid = IVAL(blob->data, 4); + len = IVAL(blob->data, 8); + blob2.data = blob->data+12; + blob2.length = MIN(blob->length, len); + trans2_pull_blob_string(req, &blob2, 0, + &st->rename_information.in.new_name, 0); + return NT_STATUS_OK; + } + + case RAW_SFILEINFO_POSITION_INFORMATION: + CHECK_MIN_BLOB_SIZE(blob, 8); + st->position_information.in.position = BVAL(blob->data, 0); + return NT_STATUS_OK; + + case RAW_SFILEINFO_MODE_INFORMATION: + CHECK_MIN_BLOB_SIZE(blob, 4); + st->mode_information.in.mode = IVAL(blob->data, 0); + return NT_STATUS_OK; + } + + return NT_STATUS_INVALID_LEVEL; +} + +/* + trans2 setfileinfo implementation +*/ +static NTSTATUS trans2_setfileinfo(struct request_context *req, struct smb_trans2 *trans) +{ + union smb_setfileinfo st; + NTSTATUS status; + uint16 level, fnum; + DATA_BLOB *blob; + + /* make sure we got enough parameters */ + if (trans->in.params.length < 4) { + return NT_STATUS_FOOBAR; + } + + fnum = SVAL(trans->in.params.data, 0); + level = SVAL(trans->in.params.data, 2); + + blob = &trans->in.data; + + st.generic.file.fnum = fnum; + st.generic.level = (enum setfileinfo_level)level; + + status = trans2_parse_sfileinfo(req, &st, blob); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = req->conn->ntvfs_ops->setfileinfo(req, &st); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + trans2_setup_reply(req, trans, 2, 0, 0); + SSVAL(trans->out.params.data, 0, 0); + return NT_STATUS_OK; +} + +/* + trans2 setpathinfo implementation +*/ +static NTSTATUS trans2_setpathinfo(struct request_context *req, struct smb_trans2 *trans) +{ + union smb_setfileinfo st; + NTSTATUS status; + uint16 level; + DATA_BLOB *blob; + + /* make sure we got enough parameters */ + if (trans->in.params.length < 4) { + return NT_STATUS_FOOBAR; + } + + level = SVAL(trans->in.params.data, 0); + blob = &trans->in.data; + st.generic.level = (enum setfileinfo_level)level; + + trans2_pull_blob_string(req, &trans->in.params, 4, &st.generic.file.fname, 0); + if (st.generic.file.fname == NULL) { + return NT_STATUS_FOOBAR; + } + + status = trans2_parse_sfileinfo(req, &st, blob); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = req->conn->ntvfs_ops->setpathinfo(req, &st); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + trans2_setup_reply(req, trans, 2, 0, 0); + SSVAL(trans->out.params.data, 0, 0); + return NT_STATUS_OK; +} + + +/* a structure to encapsulate the state information about an in-progress ffirst/fnext operation */ +struct find_state { + struct request_context *req; + struct smb_trans2 *trans; + enum search_level level; + uint16 last_entry_offset; +}; + +/* + fill a single entry in a trans2 find reply +*/ +static void find_fill_info(struct request_context *req, + struct smb_trans2 *trans, + enum search_level level, + union smb_search_data *file) +{ + char *data; + uint_t ofs = trans->out.data.length; + + switch (level) { + case RAW_SEARCH_SEARCH: + case RAW_SEARCH_GENERIC: + /* handled elsewhere */ + break; + + case RAW_SEARCH_STANDARD: + trans2_grow_data(req, trans, ofs + 23); + data = trans->out.data.data + ofs; + put_dos_date2(data, 0, file->standard.create_time); + put_dos_date2(data, 4, file->standard.access_time); + put_dos_date2(data, 8, file->standard.write_time); + SIVAL(data, 12, file->standard.size); + SIVAL(data, 16, file->standard.alloc_size); + SSVAL(data, 20, file->standard.attrib); + trans2_append_data_string(req, trans, &file->standard.name, + ofs + 22, STR_LEN8BIT | STR_TERMINATE | STR_LEN_NOTERM); + break; + + case RAW_SEARCH_EA_SIZE: + trans2_grow_data(req, trans, ofs + 27); + data = trans->out.data.data + ofs; + put_dos_date2(data, 0, file->ea_size.create_time); + put_dos_date2(data, 4, file->ea_size.access_time); + put_dos_date2(data, 8, file->ea_size.write_time); + SIVAL(data, 12, file->ea_size.size); + SIVAL(data, 16, file->ea_size.alloc_size); + SSVAL(data, 20, file->ea_size.attrib); + SIVAL(data, 22, file->ea_size.ea_size); + trans2_append_data_string(req, trans, &file->ea_size.name, + ofs + 26, STR_LEN8BIT | STR_NOALIGN); + break; + + case RAW_SEARCH_DIRECTORY_INFO: + trans2_grow_data(req, trans, ofs + 64); + data = trans->out.data.data + ofs; + SIVAL(data, 4, file->directory_info.file_index); + push_nttime(data, 8, &file->directory_info.create_time); + push_nttime(data, 16, &file->directory_info.access_time); + push_nttime(data, 24, &file->directory_info.write_time); + push_nttime(data, 32, &file->directory_info.change_time); + SBVAL(data, 40, file->directory_info.size); + SBVAL(data, 48, file->directory_info.alloc_size); + SIVAL(data, 56, file->directory_info.attrib); + trans2_append_data_string(req, trans, &file->directory_info.name, + ofs + 60, 0); + data = trans->out.data.data + ofs; + SIVAL(data, 0, trans->out.data.length - ofs); + break; + + case RAW_SEARCH_FULL_DIRECTORY_INFO: + trans2_grow_data(req, trans, ofs + 68); + data = trans->out.data.data + ofs; + SIVAL(data, 4, file->full_directory_info.file_index); + push_nttime(data, 8, &file->full_directory_info.create_time); + push_nttime(data, 16, &file->full_directory_info.access_time); + push_nttime(data, 24, &file->full_directory_info.write_time); + push_nttime(data, 32, &file->full_directory_info.change_time); + SBVAL(data, 40, file->full_directory_info.size); + SBVAL(data, 48, file->full_directory_info.alloc_size); + SIVAL(data, 56, file->full_directory_info.attrib); + SIVAL(data, 64, file->full_directory_info.ea_size); + trans2_append_data_string(req, trans, &file->full_directory_info.name, + ofs + 60, 0); + data = trans->out.data.data + ofs; + SIVAL(data, 0, trans->out.data.length - ofs); + break; + + case RAW_SEARCH_NAME_INFO: + trans2_grow_data(req, trans, ofs + 12); + data = trans->out.data.data + ofs; + SIVAL(data, 4, file->name_info.file_index); + trans2_append_data_string(req, trans, &file->name_info.name, + ofs + 8, 0); + data = trans->out.data.data + ofs; + SIVAL(data, 0, trans->out.data.length - ofs); + break; + + case RAW_SEARCH_BOTH_DIRECTORY_INFO: + trans2_grow_data(req, trans, ofs + 94); + data = trans->out.data.data + ofs; + SIVAL(data, 4, file->both_directory_info.file_index); + push_nttime(data, 8, &file->both_directory_info.create_time); + push_nttime(data, 16, &file->both_directory_info.access_time); + push_nttime(data, 24, &file->both_directory_info.write_time); + push_nttime(data, 32, &file->both_directory_info.change_time); + SBVAL(data, 40, file->both_directory_info.size); + SBVAL(data, 48, file->both_directory_info.alloc_size); + SIVAL(data, 56, file->both_directory_info.attrib); + SIVAL(data, 64, file->both_directory_info.ea_size); + SCVAL(data, 69, 0); /* reserved */ + memset(data+70,0,24); + trans2_push_data_string(req, trans, + 68 + ofs, 70 + ofs, + &file->both_directory_info.short_name, + 24, STR_UNICODE | STR_LEN8BIT); + trans2_append_data_string(req, trans, &file->both_directory_info.name, + ofs + 60, 0); + data = trans->out.data.data + ofs; + SIVAL(data, 0, trans->out.data.length - ofs); + break; + + case RAW_SEARCH_261: + trans2_grow_data(req, trans, ofs + 80); + data = trans->out.data.data + ofs; + SIVAL(data, 4, file->level_261.file_index); + push_nttime(data, 8, &file->level_261.create_time); + push_nttime(data, 16, &file->level_261.access_time); + push_nttime(data, 24, &file->level_261.write_time); + push_nttime(data, 32, &file->level_261.change_time); + SBVAL(data, 40, file->level_261.size); + SBVAL(data, 48, file->level_261.alloc_size); + SIVAL(data, 56, file->level_261.attrib); + SIVAL(data, 64, file->level_261.ea_size); + SIVAL(data, 68, file->level_261.unknown[0]); + SIVAL(data, 72, file->level_261.unknown[1]); + SIVAL(data, 76, file->level_261.unknown[2]); + trans2_append_data_string(req, trans, &file->level_261.name, + ofs + 60, 0); + data = trans->out.data.data + ofs; + SIVAL(data, 0, trans->out.data.length - ofs); + break; + + case RAW_SEARCH_262: + trans2_grow_data(req, trans, ofs + 104); + data = trans->out.data.data + ofs; + SIVAL(data, 4, file->level_262.file_index); + push_nttime(data, 8, &file->level_262.create_time); + push_nttime(data, 16, &file->level_262.access_time); + push_nttime(data, 24, &file->level_262.write_time); + push_nttime(data, 32, &file->level_262.change_time); + SBVAL(data, 40, file->level_262.size); + SBVAL(data, 48, file->level_262.alloc_size); + SIVAL(data, 56, file->level_262.attrib); + SIVAL(data, 64, file->level_262.ea_size); + SCVAL(data, 69, 0); /* reserved */ + memset(data+70,0,24); + trans2_push_data_string(req, trans, + 68 + ofs, 70 + ofs, + &file->level_262.short_name, + 24, STR_UNICODE | STR_LEN8BIT); + SIVAL(data, 94, file->level_262.unknown[0]); + SIVAL(data, 98, file->level_262.unknown[1]); + SSVAL(data, 102, 0); /* reserved? */ + trans2_append_data_string(req, trans, &file->level_262.name, + ofs + 60, 0); + data = trans->out.data.data + ofs; + SIVAL(data, 0, trans->out.data.length - ofs); + break; + } +} + +/* callback function for trans2 findfirst/findnext */ +static BOOL find_callback(void *private, union smb_search_data *file) +{ + struct find_state *state = (struct find_state *)private; + struct smb_trans2 *trans = state->trans; + uint_t old_length; + + old_length = trans->out.data.length; + + find_fill_info(state->req, trans, state->level, file); + + /* see if we have gone beyond the user specified maximum */ + if (trans->out.data.length > trans->in.max_data) { + /* restore the old length and tell the backend to stop */ + trans2_grow_data(state->req, trans, old_length); + return False; + } + + state->last_entry_offset = old_length; + return True; +} + + +/* + trans2 findfirst implementation +*/ +static NTSTATUS trans2_findfirst(struct request_context *req, struct smb_trans2 *trans) +{ + union smb_search_first search; + NTSTATUS status; + uint16 level; + char *param; + struct find_state state; + + /* make sure we got all the parameters */ + if (trans->in.params.length < 14) { + return NT_STATUS_FOOBAR; + } + + search.t2ffirst.in.search_attrib = SVAL(trans->in.params.data, 0); + search.t2ffirst.in.max_count = SVAL(trans->in.params.data, 2); + search.t2ffirst.in.flags = SVAL(trans->in.params.data, 4); + level = SVAL(trans->in.params.data, 6); + search.t2ffirst.in.storage_type = IVAL(trans->in.params.data, 8); + + trans2_pull_blob_string(req, &trans->in.params, 12, &search.t2ffirst.in.pattern, 0); + if (search.t2ffirst.in.pattern == NULL) { + return NT_STATUS_FOOBAR; + } + + search.t2ffirst.level = (enum search_level)level; + if (search.t2ffirst.level >= RAW_SEARCH_GENERIC) { + return NT_STATUS_INVALID_LEVEL; + } + + /* setup the private state structure that the backend will give us in the callback */ + state.req = req; + state.trans = trans; + state.level = search.t2ffirst.level; + state.last_entry_offset = 0; + + /* setup for just a header in the reply */ + trans2_setup_reply(req, trans, 10, 0, 0); + + /* call the backend */ + status = req->conn->ntvfs_ops->search_first(req, &search, &state, find_callback); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* fill in the findfirst reply header */ + param = trans->out.params.data; + SSVAL(param, VWV(0), search.t2ffirst.out.handle); + SSVAL(param, VWV(1), search.t2ffirst.out.count); + SSVAL(param, VWV(2), search.t2ffirst.out.end_of_search); + SSVAL(param, VWV(3), 0); + SSVAL(param, VWV(4), state.last_entry_offset); + + return NT_STATUS_OK; +} + + +/* + trans2 findnext implementation +*/ +static NTSTATUS trans2_findnext(struct request_context *req, struct smb_trans2 *trans) +{ + union smb_search_next search; + NTSTATUS status; + uint16 level; + char *param; + struct find_state state; + + /* make sure we got all the parameters */ + if (trans->in.params.length < 12) { + return NT_STATUS_FOOBAR; + } + + search.t2fnext.in.handle = SVAL(trans->in.params.data, 0); + search.t2fnext.in.max_count = SVAL(trans->in.params.data, 2); + level = SVAL(trans->in.params.data, 4); + search.t2fnext.in.resume_key = IVAL(trans->in.params.data, 6); + search.t2fnext.in.flags = SVAL(trans->in.params.data, 10); + + trans2_pull_blob_string(req, &trans->in.params, 12, &search.t2fnext.in.last_name, 0); + if (search.t2fnext.in.last_name == NULL) { + return NT_STATUS_FOOBAR; + } + + search.t2fnext.level = (enum search_level)level; + if (search.t2fnext.level >= RAW_SEARCH_GENERIC) { + return NT_STATUS_INVALID_LEVEL; + } + + /* setup the private state structure that the backend will give us in the callback */ + state.req = req; + state.trans = trans; + state.level = search.t2fnext.level; + state.last_entry_offset = 0; + + /* setup for just a header in the reply */ + trans2_setup_reply(req, trans, 8, 0, 0); + + /* call the backend */ + status = req->conn->ntvfs_ops->search_next(req, &search, &state, find_callback); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* fill in the findfirst reply header */ + param = trans->out.params.data; + SSVAL(param, VWV(0), search.t2fnext.out.count); + SSVAL(param, VWV(1), search.t2fnext.out.end_of_search); + SSVAL(param, VWV(2), 0); + SSVAL(param, VWV(3), state.last_entry_offset); + + return NT_STATUS_OK; +} + + +/* + backend for trans2 requests +*/ +static NTSTATUS trans2_backend(struct request_context *req, struct smb_trans2 *trans) +{ + if (req->conn->ntvfs_ops->trans2 != NULL) { + /* direct trans2 pass thru */ + return req->conn->ntvfs_ops->trans2(req, trans); + } + + /* must have at least one setup word */ + if (trans->in.setup_count < 1) { + return NT_STATUS_FOOBAR; + } + + /* the trans2 command is in setup[0] */ + switch (trans->in.setup[0]) { + case TRANSACT2_FINDFIRST: + return trans2_findfirst(req, trans); + case TRANSACT2_FINDNEXT: + return trans2_findnext(req, trans); + case TRANSACT2_QPATHINFO: + return trans2_qpathinfo(req, trans); + case TRANSACT2_QFILEINFO: + return trans2_qfileinfo(req, trans); + case TRANSACT2_SETFILEINFO: + return trans2_setfileinfo(req, trans); + case TRANSACT2_SETPATHINFO: + return trans2_setpathinfo(req, trans); + case TRANSACT2_QFSINFO: + return trans2_qfsinfo(req, trans); + } + + /* an unknown trans2 command */ + return NT_STATUS_FOOBAR; +} + + +/**************************************************************************** + Reply to an SMBtrans2 request +****************************************************************************/ +void reply_trans2(struct request_context *req) +{ + struct smb_trans2 trans; + int i; + uint16 param_ofs, data_ofs; + uint16 param_count, data_count; + uint16 params_left, data_left; + uint16 param_total, data_total; + char *params, *data; + NTSTATUS status; + + /* parse request */ + if (req->in.wct < 14) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + param_total = SVAL(req->in.vwv, VWV(0)); + data_total = SVAL(req->in.vwv, VWV(1)); + trans.in.max_param = SVAL(req->in.vwv, VWV(2)); + trans.in.max_data = SVAL(req->in.vwv, VWV(3)); + trans.in.max_setup = CVAL(req->in.vwv, VWV(4)); + trans.in.flags = SVAL(req->in.vwv, VWV(5)); + trans.in.timeout = IVAL(req->in.vwv, VWV(6)); + param_count = SVAL(req->in.vwv, VWV(9)); + param_ofs = SVAL(req->in.vwv, VWV(10)); + data_count = SVAL(req->in.vwv, VWV(11)); + data_ofs = SVAL(req->in.vwv, VWV(12)); + trans.in.setup_count = CVAL(req->in.vwv, VWV(13)); + + if (req->in.wct != 14 + trans.in.setup_count) { + req_reply_dos_error(req, ERRSRV, ERRerror); + return; + } + + /* parse out the setup words */ + trans.in.setup = talloc(req->mem_ctx, trans.in.setup_count * sizeof(uint16)); + if (!trans.in.setup) { + req_reply_error(req, NT_STATUS_NO_MEMORY); + return; + } + for (i=0;iin.vwv, VWV(14+i)); + } + + if (!req_pull_blob(req, req->in.hdr + param_ofs, param_count, &trans.in.params) || + !req_pull_blob(req, req->in.hdr + data_ofs, data_count, &trans.in.data)) { + req_reply_error(req, NT_STATUS_FOOBAR); + return; + } + + /* is it a partial request? if so, then send a 'send more' message */ + if (param_total > param_count || + data_total > data_count) { + DEBUG(0,("REWRITE: not handling partial trans2 requests!\n")); + return; + } + + /* its a full request, give it to the backend */ + status = trans2_backend(req, &trans); + + if (!NT_STATUS_IS_OK(status)) { + req_reply_error(req, status); + return; + } + + params_left = trans.out.params.length; + data_left = trans.out.data.length; + params = trans.out.params.data; + data = trans.out.data.data; + + req->control_flags |= REQ_CONTROL_PROTECTED; + + /* we need to divide up the reply into chunks that fit into + the negotiated buffer size */ + do { + uint16 this_data, this_param, max_bytes; + uint_t align1 = 1, align2 = (params_left ? 2 : 0); + + req_setup_reply(req, 10 + trans.out.setup_count, 0); + + max_bytes = req_max_data(req) - (align1 + align2); + + this_param = params_left; + if (this_param > max_bytes) { + this_param = max_bytes; + } + max_bytes -= this_param; + + this_data = data_left; + if (this_data > max_bytes) { + this_data = max_bytes; + } + + req_grow_data(req, this_param + this_data + (align1 + align2)); + + SSVAL(req->out.vwv, VWV(0), trans.out.params.length); + SSVAL(req->out.vwv, VWV(1), trans.out.data.length); + SSVAL(req->out.vwv, VWV(2), 0); + + SSVAL(req->out.vwv, VWV(3), this_param); + SSVAL(req->out.vwv, VWV(4), align1 + PTR_DIFF(req->out.data, req->out.hdr)); + SSVAL(req->out.vwv, VWV(5), PTR_DIFF(params, trans.out.params.data)); + + SSVAL(req->out.vwv, VWV(6), this_data); + SSVAL(req->out.vwv, VWV(7), align1 + align2 + + PTR_DIFF(req->out.data + this_param, req->out.hdr)); + SSVAL(req->out.vwv, VWV(8), PTR_DIFF(data, trans.out.data.data)); + + SSVAL(req->out.vwv, VWV(9), trans.out.setup_count); + for (i=0;iout.vwv, VWV(10+i), trans.out.setup[i]); + } + + memset(req->out.data, 0, align1); + if (this_param != 0) { + memcpy(req->out.data + align1, params, this_param); + } + memset(req->out.data+this_param+align1, 0, align2); + if (this_data != 0) { + memcpy(req->out.data+this_param+align1+align2, data, this_data); + } + + params_left -= this_param; + data_left -= this_data; + params += this_param; + data += this_data; + + /* if this is the last chunk then the request can be destroyed */ + if (params_left == 0 && data_left == 0) { + req->control_flags &= ~REQ_CONTROL_PROTECTED; + } + + req_send_reply(req); + } while (params_left != 0 || data_left != 0); +} diff --git a/source4/smbwrapper/.cvsignore b/source4/smbwrapper/.cvsignore new file mode 100644 index 0000000000..7835612d32 --- /dev/null +++ b/source4/smbwrapper/.cvsignore @@ -0,0 +1,8 @@ +*.po +*.po32 +kernel_stat.h +smbsh +tst +tst.c +wrapper.h +xstat.c diff --git a/source4/smbwrapper/PORTING b/source4/smbwrapper/PORTING new file mode 100644 index 0000000000..884246d078 --- /dev/null +++ b/source4/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 + +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 +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/source4/smbwrapper/README b/source4/smbwrapper/README new file mode 100644 index 0000000000..8d5c376f82 --- /dev/null +++ b/source4/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/source4/smbwrapper/realcalls.c b/source4/smbwrapper/realcalls.c new file mode 100644 index 0000000000..b64f4a43db --- /dev/null +++ b/source4/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/source4/smbwrapper/realcalls.h b/source4/smbwrapper/realcalls.h new file mode 100644 index 0000000000..6c230dba05 --- /dev/null +++ b/source4/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 +#elif HAVE_SYSCALL_H +#include +#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/source4/smbwrapper/shared.c b/source4/smbwrapper/shared.c new file mode 100644 index 0000000000..b4cfcf7148 --- /dev/null +++ b/source4/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); + + pstrcpy(&variables[shared_size] + 4, name); + pstrcpy(&variables[shared_size] + 4 + l1, val); + + 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/source4/smbwrapper/smbsh.c b/source4/smbwrapper/smbsh.c new file mode 100644 index 0000000000..221c6d87c2 --- /dev/null +++ b/source4/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_BINDIR; + 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/source4/smbwrapper/smbsh.in b/source4/smbwrapper/smbsh.in new file mode 100644 index 0000000000..323f091699 --- /dev/null +++ b/source4/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/source4/smbwrapper/smbw.c b/source4/smbwrapper/smbw.c new file mode 100644 index 0000000000..fbc69b2371 --- /dev/null +++ b/source4/smbwrapper/smbw.c @@ -0,0 +1,1554 @@ +/* + 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;inext) { + 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, lp_netbios_name(), 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; + st64->st_blksize = st->st_blksize; + st64->st_blocks = st->st_blocks; +} +#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; + st->st_blksize = kbuf->st_blksize; + st->st_blocks = kbuf->st_blocks; + st->st_atime = kbuf->st_atime; + st->st_mtime = kbuf->st_mtime; + st->st_ctime = kbuf->st_ctime; +} +#endif diff --git a/source4/smbwrapper/smbw.h b/source4/smbwrapper/smbw.h new file mode 100644 index 0000000000..3f0b1cbb44 --- /dev/null +++ b/source4/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/source4/smbwrapper/smbw_cache.c b/source4/smbwrapper/smbw_cache.c new file mode 100644 index 0000000000..fcb0eda805 --- /dev/null +++ b/source4/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/source4/smbwrapper/smbw_dir.c b/source4/smbwrapper/smbw_dir.c new file mode 100644 index 0000000000..31d81a1e7e --- /dev/null +++ b/source4/smbwrapper/smbw_dir.c @@ -0,0 +1,688 @@ +/* + 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) { + 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) && + 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/source4/smbwrapper/smbw_stat.c b/source4/smbwrapper/smbw_stat.c new file mode 100644 index 0000000000..6c476a8a67 --- /dev/null +++ b/source4/smbwrapper/smbw_stat.c @@ -0,0 +1,250 @@ +/* + 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; + st->st_blksize = 512; + st->st_blocks = (size+511)/512; + 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/source4/smbwrapper/wrapped.c b/source4/smbwrapper/wrapped.c new file mode 100644 index 0000000000..338ee0d5b1 --- /dev/null +++ b/source4/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 +#include +#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/source4/tdb/.cvsignore b/source4/tdb/.cvsignore new file mode 100644 index 0000000000..66445fe269 --- /dev/null +++ b/source4/tdb/.cvsignore @@ -0,0 +1,11 @@ +*.po +*.po32 +tdbbackup +tdbdump +tdbtest +tdbtool +tdbtorture +test.db +test.gdbm +test.tdb +torture.tdb diff --git a/source4/tdb/Makefile b/source4/tdb/Makefile new file mode 100644 index 0000000000..59fbb079bd --- /dev/null +++ b/source4/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/source4/tdb/README b/source4/tdb/README new file mode 100644 index 0000000000..fac3eacb4d --- /dev/null +++ b/source4/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/source4/tdb/spinlock.c b/source4/tdb/spinlock.c new file mode 100644 index 0000000000..2370ce3bdd --- /dev/null +++ b/source4/tdb/spinlock.c @@ -0,0 +1,430 @@ +/* + Unix SMB/CIFS implementation. + Samba database functions + Copyright (C) Anton Blanchard 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. +*/ +#if HAVE_CONFIG_H +#include +#endif + +#if STANDALONE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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) + +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) +{ + return 0; +} + +/* + * 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 = (hash_size + 1) * sizeof(tdb_rwlock_t); + 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/source4/tdb/spinlock.h b/source4/tdb/spinlock.h new file mode 100644 index 0000000000..d6a2ac6eb8 --- /dev/null +++ b/source4/tdb/spinlock.h @@ -0,0 +1,55 @@ +#ifndef __SPINLOCK_H__ +#define __SPINLOCK_H__ + +#if HAVE_CONFIG_H +#include +#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); + +#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); +#endif + +#endif diff --git a/source4/tdb/tdb.c b/source4/tdb/tdb.c new file mode 100644 index 0000000000..097209ff7a --- /dev/null +++ b/source4/tdb/tdb.c @@ -0,0 +1,2020 @@ + /* + Unix SMB/CIFS implementation. + Samba database functions + Copyright (C) Andrew Tridgell 1999-2000 + Copyright (C) Luke Kenneth Casson Leighton 2000 + Copyright (C) Paul `Rusty' Russell 2000 + Copyright (C) Jeremy Allison 2000-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. +*/ +#ifdef STANDALONE +#if HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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)) + +/* 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) + return TDB_ERRCODE(TDB_ERR_LOCK_TIMEOUT, -1); + /* Otherwise - generic lock error. */ + /* errno set by fcntl */ + 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;iheader.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_HASH_TOP(tdb->header.hash_size-1)) { + 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; + + 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(TDB_CONTEXT *tdb, TDB_DATA key, int locktype, + struct list_struct *rec) +{ + u32 hash, rec_ptr; + + hash = tdb_hash(&key); + 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(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf) +{ + struct list_struct rec; + tdb_off rec_ptr; + + /* find entry */ + if (!(rec_ptr = tdb_find(tdb, key, tdb_hash(&key), &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; + + /* find which hash bucket it is in */ + if (!(rec_ptr = tdb_find_lock(tdb,key,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 +*/ +int tdb_exists(TDB_CONTEXT *tdb, TDB_DATA key) +{ + struct list_struct rec; + + if (tdb_find_lock(tdb, key, F_RDLCK, &rec) == 0) + return 0; + tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK); + return 1; +} + +/* 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 (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 *state) +{ + 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, state)) { + /* 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(tdb, 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 */ +int tdb_delete(TDB_CONTEXT *tdb, TDB_DATA key) +{ + tdb_off rec_ptr; + struct list_struct rec; + int ret; + + if (!(rec_ptr = tdb_find_lock(tdb, key, 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; +} + +/* 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(tdb, key)) { + tdb->ecode = TDB_ERR_EXISTS; + goto fail; + } + } else { + /* first try in-place update, on modify or replace. */ + if (tdb_update(tdb, key, dbuf) == 0) + goto out; + if (flag == TDB_MODIFY && tdb->ecode == TDB_ERR_NOEXIST) + 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(tdb, key); + + /* 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); + + /* now we're into insert / modify / replace of a record which + * we know could not be optimised by an in-place store (for + * various reasons). */ + 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, TDB_DATA new_dbuf) +{ + struct list_struct rec; + tdb_off rec_ptr; + + /* find entry */ + if (!(rec_ptr = tdb_find(tdb, key, tdb_hash(&key), &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, 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(tdb, key); + + 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; + 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 ((locked = (tdb_brlock(tdb, ACTIVE_LOCK, F_WRLCK, F_SETLK, 0) == 0)) + && (tdb_flags & TDB_CLEAR_IF_FIRST)) { + 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 + || (tdb->header.hash_size != hash_size + && !(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; + } + } + /* 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; + 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 is 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_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_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) { + if (tdb_reopen(tdb) != 0) return -1; + } + + return 0; +} diff --git a/source4/tdb/tdb.h b/source4/tdb/tdb.h new file mode 100644 index 0000000000..6f3b1ff756 --- /dev/null +++ b/source4/tdb/tdb.h @@ -0,0 +1,144 @@ +#ifndef __TDB_H__ +#define __TDB_H__ + +/* + Unix SMB/CIFS implementation. + Samba database functions + 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. +*/ + +#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_NOEXIST, TDB_ERR_NOLOCK, TDB_ERR_LOCK_TIMEOUT }; + +#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 *state); +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/source4/tdb/tdb.magic b/source4/tdb/tdb.magic new file mode 100644 index 0000000000..f5619e7327 --- /dev/null +++ b/source4/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/source4/tdb/tdbbackup.c b/source4/tdb/tdbbackup.c new file mode 100644 index 0000000000..7b344de6c4 --- /dev/null +++ b/source4/tdb/tdbbackup.c @@ -0,0 +1,315 @@ +/* + 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. + + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tdb.h" + +static int failed; + +static 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); + } + strncpy(ret, name, len); + strncat(ret, suffix, len); + 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 +*/ +static 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; + } + + printf("%s : %d records\n", old_name, count1); + free(tmp_name); + + return 0; +} + + + +/* + verify a tdb and if it is corrupt then restore from *.bak +*/ +static 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; +} + + +/* + 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] \n\n"); + printf(" -h this help message\n"); + printf(" -s suffix set the backup suffix\n"); + printf(" -v veryify mode (restore if corrupt)\n"); +} + + + int main(int argc, char *argv[]) +{ + int i; + int ret = 0; + int c; + int verify = 0; + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 \n"); + exit(1); + } + + fname = argv[1]; + + return dump_tdb(fname); +} diff --git a/source4/tdb/tdbtest.c b/source4/tdb/tdbtest.c new file mode 100644 index 0000000000..89295a3291 --- /dev/null +++ b/source4/tdb/tdbtest.c @@ -0,0 +1,263 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tdb.h" +#include + +/* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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;i8) 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(" +tdbtool: + create dbname : create a database + open dbname : open an existing database + erase : erase the database + dump : dump the database as strings + insert key data : insert a record + store key data : store a record (replace) + show key : show a record by key + delete key : delete a record by key + list : print the database hash table and freelist + free : print the database freelist + 1 | first : print the first record + n | next : print the next record + q | quit : terminate + \\n : repeat 'next' command +"); +} + +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;*/ + 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); +} + +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"); + } +} + +#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,"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/source4/tdb/tdbtorture.c b/source4/tdb/tdbtorture.c new file mode 100644 index 0000000000..e27bbff990 --- /dev/null +++ b/source4/tdb/tdbtorture.c @@ -0,0 +1,226 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 + +/* 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_by_string(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_by_string(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_by_string(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 becouse 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 becouse 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; + 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 'w': + len = 2; + w = (uint16)va_arg(ap, int); + if (bufsize >= len) + SSVAL(buf, 0, w); + break; + case 'd': + len = 4; + d = va_arg(ap, uint32); + if (bufsize >= len) + SIVAL(buf, 0, d); + break; + case 'p': + len = 4; + p = va_arg(ap, void *); + d = p?1:0; + if (bufsize >= len) + SIVAL(buf, 0, d); + break; + case 'P': + s = va_arg(ap,char *); + w = strlen(s); + len = w + 1; + if (bufsize >= len) + memcpy(buf, s, len); + break; + case 'f': + s = va_arg(ap,char *); + w = strlen(s); + len = w + 1; + if (bufsize >= len) + memcpy(buf, s, len); + break; + case 'B': + i = va_arg(ap, int); + s = va_arg(ap, char *); + len = 4+i; + if (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; + bufsize -= len; + } + + 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; + 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 '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; +} + +/**************************************************************************** + 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 = next_node; + }; +}; + + diff --git a/source4/tdb/tdbutil.h b/source4/tdb/tdbutil.h new file mode 100644 index 0000000000..01473446a1 --- /dev/null +++ b/source4/tdb/tdbutil.h @@ -0,0 +1,37 @@ +/* + Unix SMB/CIFS implementation. + tdb utility functions + 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. +*/ + +#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/source4/tests/.cvsignore b/source4/tests/.cvsignore new file mode 100644 index 0000000000..ff5b9ca58d --- /dev/null +++ b/source4/tests/.cvsignore @@ -0,0 +1,3 @@ +unixsock +fcntl_lock_thread +a.out diff --git a/source4/tests/README b/source4/tests/README new file mode 100644 index 0000000000..cf1be8b00a --- /dev/null +++ b/source4/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/source4/tests/crypttest.c b/source4/tests/crypttest.c new file mode 100644 index 0000000000..efee2e593d --- /dev/null +++ b/source4/tests/crypttest.c @@ -0,0 +1,852 @@ +#if defined(HAVE_UNISTD_H) +#include +#endif + +#include + +#ifdef HAVE_STRING_H +#include +#endif + +#ifdef HAVE_STRINGS_H +#include +#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 + 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/source4/tests/fcntl_lock.c b/source4/tests/fcntl_lock.c new file mode 100644 index 0000000000..3dc12a3897 --- /dev/null +++ b/source4/tests/fcntl_lock.c @@ -0,0 +1,121 @@ +/* test whether fcntl locking works on this system */ + +#if defined(HAVE_UNISTD_H) +#include +#endif + +#include +#include +#include + +#ifdef HAVE_FCNTL_H +#include +#endif + +#ifdef HAVE_SYS_FCNTL_H +#include +#endif + +#ifdef HAVE_SYS_WAIT_H +#include +#endif + +#include + +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/source4/tests/fcntl_lock64.c b/source4/tests/fcntl_lock64.c new file mode 100644 index 0000000000..e5ecd88fd0 --- /dev/null +++ b/source4/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 +#endif + +#include +#include +#include + +#ifdef HAVE_FCNTL_H +#include +#endif + +#ifdef HAVE_SYS_FCNTL_H +#include +#endif + +#include + +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/source4/tests/fcntl_lock_thread.c b/source4/tests/fcntl_lock_thread.c new file mode 100644 index 0000000000..f31105626c --- /dev/null +++ b/source4/tests/fcntl_lock_thread.c @@ -0,0 +1,122 @@ +/* test whether fcntl locking works between threads on this Linux system */ + +#include + +#include +#include +#include + +#include + +#include + +#include + +#include +#include + +static int sys_waitpid(pid_t pid,int *status,int options) +{ + return waitpid(pid,status,options); +} + +#define DATA "conftest.fcntl" + +#define SEEK_SET 0 + +static void *test_thread(void *thread_parm) +{ + int *status = thread_parm; + int fd, ret; + struct flock lock; + + sleep(2); + fd = open(DATA, O_RDWR); + + if (fd == -1) { + fprintf(stderr,"ERROR: failed to open %s (errno=%d)\n", + DATA, (int)errno); + pthread_exit(thread_parm); + } + + lock.l_type = F_WRLCK; + lock.l_whence = SEEK_SET; + lock.l_start = 0; + lock.l_len = 4; + lock.l_pid = 0; + + /* check if a lock applies */ + ret = fcntl(fd,F_SETLK,&lock); + if ((ret != -1)) { + fprintf(stderr,"ERROR: lock test failed (ret=%d errno=%d)\n", ret, (int)errno); + } else { + *status = 0; /* SUCCESS! */ + } + pthread_exit(thread_parm); +} + +/* lock a byte range in a open file */ +int main(int argc, char *argv[]) +{ + struct flock lock; + int fd, ret, status=1, rc; + pid_t pid; + char *testdir = NULL; + pthread_t thread_id; + pthread_attr_t thread_attr; + + testdir = getenv("TESTDIR"); + if (testdir) chdir(testdir); + + alarm(10); + + pthread_attr_init(&thread_attr); + pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED); + rc = pthread_create(&thread_id, &thread_attr, &test_thread, &status); + pthread_attr_destroy(&thread_attr); + if (rc == 0) { + fprintf(stderr,"created thread_id=%lu\n", + (unsigned long int)thread_id); + } else { + fprintf(stderr,"ERROR: thread create failed, rc=%d\n", rc); + } + + unlink(DATA); + fd = open(DATA, O_RDWR|O_CREAT|O_RDWR, 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); + + sleep(4); /* allow thread to try getting lock */ + + 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/source4/tests/ftruncate.c b/source4/tests/ftruncate.c new file mode 100644 index 0000000000..93282782ee --- /dev/null +++ b/source4/tests/ftruncate.c @@ -0,0 +1,27 @@ +/* test whether ftruncte() can extend a file */ + +#if defined(HAVE_UNISTD_H) +#include +#endif + +#include +#include +#include + +#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/source4/tests/getgroups.c b/source4/tests/getgroups.c new file mode 100644 index 0000000000..343fd5a184 --- /dev/null +++ b/source4/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 +#endif + +#include +#include +#include +#include + +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 +#endif +#include +#include +#include +#include + +#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/source4/tests/shlib.c b/source4/tests/shlib.c new file mode 100644 index 0000000000..761d9fd5c5 --- /dev/null +++ b/source4/tests/shlib.c @@ -0,0 +1,6 @@ +/* a trivial function used to test building shared libraries */ + +int foo(void) +{ + return 1; +} diff --git a/source4/tests/summary.c b/source4/tests/summary.c new file mode 100644 index 0000000000..d3708c236c --- /dev/null +++ b/source4/tests/summary.c @@ -0,0 +1,30 @@ +#include + +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"); + /* REWRITE: 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/source4/tests/trivial.c b/source4/tests/trivial.c new file mode 100644 index 0000000000..2723637a0f --- /dev/null +++ b/source4/tests/trivial.c @@ -0,0 +1,4 @@ +main() +{ + exit(0); +} diff --git a/source4/tests/unixsock.c b/source4/tests/unixsock.c new file mode 100644 index 0000000000..f2765d68f6 --- /dev/null +++ b/source4/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 , June 2000. */ + +/* TODO: Look for AF_LOCAL (most standard), AF_UNIX, and AF_FILE. */ + +#include + +#ifdef HAVE_SYS_SOCKET_H +# include +#endif + +#ifdef HAVE_SYS_UN_H +# include +#endif + +#ifdef HAVE_SYS_TYPES_H +# include +#endif + +#if HAVE_SYS_WAIT_H +# include +#endif + +#if HAVE_ERRNO_DECL +# include +#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/source4/torture/.cvsignore b/source4/torture/.cvsignore new file mode 100644 index 0000000000..06cac36e47 --- /dev/null +++ b/source4/torture/.cvsignore @@ -0,0 +1 @@ +torturebad.c diff --git a/source4/torture/aliases.c b/source4/torture/aliases.c new file mode 100644 index 0000000000..feb940eb42 --- /dev/null +++ b/source4/torture/aliases.c @@ -0,0 +1,403 @@ +/* + Unix SMB/CIFS implementation. + SMB trans2 alias scanner + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 create_complex_file(struct cli_state *cli, TALLOC_CTX *mem_ctx, const char *fname); + +struct trans2_blobs { + struct trans2_blobs *next, *prev; + uint16 level; + DATA_BLOB params, data; +}; + +/* look for aliases for a query */ +static void gen_aliases(struct cli_state *cli, struct smb_trans2 *t2, int level_offset) +{ + TALLOC_CTX *mem_ctx; + uint16 level; + struct trans2_blobs *alias_blobs = NULL; + struct trans2_blobs *t2b, *t2b2; + int count=0, alias_count=0; + + mem_ctx = talloc_init("aliases"); + + for (level=0;level<2000;level++) { + NTSTATUS status; + + SSVAL(t2->in.params.data, level_offset, level); + + status = smb_raw_trans2(cli->tree, mem_ctx, t2); + if (!NT_STATUS_IS_OK(status)) continue; + + t2b = talloc(mem_ctx, sizeof(*t2b)); + t2b->level = level; + t2b->params = t2->out.params; + t2b->data = t2->out.data; + DLIST_ADD(alias_blobs, t2b); + d_printf("\tFound level %4u (0x%03x) of size %3d (0x%02x)\n", + level, level, + t2b->data.length, t2b->data.length); + count++; + } + + d_printf("Found %d levels with success status\n", count); + + for (t2b=alias_blobs; t2b; t2b=t2b->next) { + for (t2b2=alias_blobs; t2b2; t2b2=t2b2->next) { + if (t2b->level >= t2b2->level) continue; + if (data_blob_equal(&t2b->params, &t2b2->params) && + data_blob_equal(&t2b->data, &t2b2->data)) { + printf("\tLevel %u (0x%x) and level %u (0x%x) are possible aliases\n", + t2b->level, t2b->level, t2b2->level, t2b2->level); + alias_count++; + } + } + } + + d_printf("Found %d aliased levels\n", alias_count); + + talloc_destroy(mem_ctx); +} + +/* look for qfsinfo aliases */ +static void qfsinfo_aliases(struct cli_state *cli) +{ + struct smb_trans2 t2; + uint16 setup = TRANSACT2_QFSINFO; + + d_printf("\nChecking for QFSINFO aliases\n"); + + t2.in.max_param = 0; + t2.in.max_data = 0x8000; + t2.in.max_setup = 0; + t2.in.flags = 0; + t2.in.timeout = 0; + t2.in.setup_count = 1; + t2.in.setup = &setup; + t2.in.params = data_blob(NULL, 2); + t2.in.data = data_blob(NULL, 0); + + gen_aliases(cli, &t2, 0); +} + +/* look for qfileinfo aliases */ +static void qfileinfo_aliases(struct cli_state *cli) +{ + struct smb_trans2 t2; + uint16 setup = TRANSACT2_QFILEINFO; + const char *fname = "\\qfileinfo_aliases.txt"; + int fnum; + + d_printf("\nChecking for QFILEINFO aliases\n"); + + t2.in.max_param = 2; + t2.in.max_data = 0x8000; + t2.in.max_setup = 0; + t2.in.flags = 0; + t2.in.timeout = 0; + t2.in.setup_count = 1; + t2.in.setup = &setup; + t2.in.params = data_blob(NULL, 4); + t2.in.data = data_blob(NULL, 0); + + cli_unlink(cli, fname); + fnum = create_complex_file(cli, cli->mem_ctx, fname); + if (fnum == -1) { + printf("ERROR: open of %s failed (%s)\n", fname, cli_errstr(cli)); + } + + cli_write(cli, fnum, 0, (char *)&t2, 0, sizeof(t2)); + + SSVAL(t2.in.params.data, 0, fnum); + + gen_aliases(cli, &t2, 2); + + cli_close(cli, fnum); + cli_unlink(cli, fname); +} + + +/* look for qpathinfo aliases */ +static void qpathinfo_aliases(struct cli_state *cli) +{ + struct smb_trans2 t2; + uint16 setup = TRANSACT2_QPATHINFO; + const char *fname = "\\qpathinfo_aliases.txt"; + int fnum; + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_init("qpathinfo"); + + d_printf("\nChecking for QPATHINFO aliases\n"); + + t2.in.max_param = 2; + t2.in.max_data = 0x8000; + t2.in.max_setup = 0; + t2.in.flags = 0; + t2.in.timeout = 0; + t2.in.setup_count = 1; + t2.in.setup = &setup; + t2.in.params = data_blob_talloc(mem_ctx, NULL, 6); + t2.in.data = data_blob(NULL, 0); + + cli_unlink(cli, fname); + fnum = create_complex_file(cli, cli->mem_ctx, fname); + if (fnum == -1) { + printf("ERROR: open of %s failed (%s)\n", fname, cli_errstr(cli)); + } + + cli_write(cli, fnum, 0, (char *)&t2, 0, sizeof(t2)); + cli_close(cli, fnum); + + SIVAL(t2.in.params.data, 2, 0); + + cli_blob_append_string(cli->session, mem_ctx, &t2.in.params, + fname, STR_TERMINATE); + + gen_aliases(cli, &t2, 0); + + cli_unlink(cli, fname); + talloc_destroy(mem_ctx); +} + + +/* look for trans2 findfirst aliases */ +static void findfirst_aliases(struct cli_state *cli) +{ + struct smb_trans2 t2; + uint16 setup = TRANSACT2_FINDFIRST; + const char *fname = "\\findfirst_aliases.txt"; + int fnum; + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_init("findfirst"); + + d_printf("\nChecking for FINDFIRST aliases\n"); + + t2.in.max_param = 16; + t2.in.max_data = 0x8000; + t2.in.max_setup = 0; + t2.in.flags = 0; + t2.in.timeout = 0; + t2.in.setup_count = 1; + t2.in.setup = &setup; + t2.in.params = data_blob_talloc(mem_ctx, NULL, 12); + t2.in.data = data_blob(NULL, 0); + + cli_unlink(cli, fname); + fnum = create_complex_file(cli, cli->mem_ctx, fname); + if (fnum == -1) { + printf("ERROR: open of %s failed (%s)\n", fname, cli_errstr(cli)); + } + + cli_write(cli, fnum, 0, (char *)&t2, 0, sizeof(t2)); + cli_close(cli, fnum); + + SSVAL(t2.in.params.data, 0, 0); + SSVAL(t2.in.params.data, 2, 1); + SSVAL(t2.in.params.data, 4, FLAG_TRANS2_FIND_CLOSE); + SSVAL(t2.in.params.data, 6, 0); + SIVAL(t2.in.params.data, 8, 0); + + cli_blob_append_string(cli->session, mem_ctx, &t2.in.params, + fname, STR_TERMINATE); + + gen_aliases(cli, &t2, 6); + + cli_unlink(cli, fname); + talloc_destroy(mem_ctx); +} + + + +/* look for aliases for a set function */ +static void gen_set_aliases(struct cli_state *cli, struct smb_trans2 *t2, int level_offset) +{ + TALLOC_CTX *mem_ctx; + uint16 level; + struct trans2_blobs *alias_blobs = NULL; + struct trans2_blobs *t2b; + int count=0, dsize; + + mem_ctx = talloc_init("aliases"); + + for (level=1;level<1100;level++) { + NTSTATUS status, status1; + SSVAL(t2->in.params.data, level_offset, level); + + status1 = NT_STATUS_OK; + + for (dsize=2; dsize<1024; dsize += 2) { + data_blob_free(&t2->in.data); + t2->in.data = data_blob(NULL, dsize); + data_blob_clear(&t2->in.data); + status = smb_raw_trans2(cli->tree, mem_ctx, t2); + /* some error codes mean that this whole level doesn't exist */ + if (NT_STATUS_EQUAL(NT_STATUS_INVALID_LEVEL, status) || + NT_STATUS_EQUAL(NT_STATUS_INVALID_INFO_CLASS, status) || + NT_STATUS_EQUAL(NT_STATUS_NOT_SUPPORTED, status)) { + break; + } + if (NT_STATUS_IS_OK(status)) break; + + /* invalid parameter means that the level exists at this + size, but the contents are wrong (not surprising with + all zeros!) */ + if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) break; + + /* this is the usual code for 'wrong size' */ + if (NT_STATUS_EQUAL(status, NT_STATUS_INFO_LENGTH_MISMATCH)) { + continue; + } + + if (!NT_STATUS_EQUAL(status, status1)) { + printf("level=%d size=%d %s\n", level, dsize, nt_errstr(status)); + } + status1 = status; + } + + if (!NT_STATUS_IS_OK(status) && + !NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) continue; + + t2b = talloc(mem_ctx, sizeof(*t2b)); + t2b->level = level; + t2b->params = t2->out.params; + t2b->data = t2->out.data; + DLIST_ADD(alias_blobs, t2b); + d_printf("\tFound level %4u (0x%03x) of size %3d (0x%02x)\n", + level, level, + t2->in.data.length, t2->in.data.length); + count++; + } + + d_printf("Found %d valid levels\n", count); + talloc_destroy(mem_ctx); +} + + + +/* look for setfileinfo aliases */ +static void setfileinfo_aliases(struct cli_state *cli) +{ + struct smb_trans2 t2; + uint16 setup = TRANSACT2_SETFILEINFO; + const char *fname = "\\setfileinfo_aliases.txt"; + int fnum; + + d_printf("\nChecking for SETFILEINFO aliases\n"); + + t2.in.max_param = 2; + t2.in.max_data = 0; + t2.in.max_setup = 0; + t2.in.flags = 0; + t2.in.timeout = 0; + t2.in.setup_count = 1; + t2.in.setup = &setup; + t2.in.params = data_blob(NULL, 6); + t2.in.data = data_blob(NULL, 0); + + cli_unlink(cli, fname); + fnum = create_complex_file(cli, cli->mem_ctx, fname); + if (fnum == -1) { + printf("ERROR: open of %s failed (%s)\n", fname, cli_errstr(cli)); + } + + cli_write(cli, fnum, 0, (char *)&t2, 0, sizeof(t2)); + + SSVAL(t2.in.params.data, 0, fnum); + SSVAL(t2.in.params.data, 4, 0); + + gen_set_aliases(cli, &t2, 2); + + cli_close(cli, fnum); + cli_unlink(cli, fname); +} + +/* look for setpathinfo aliases */ +static void setpathinfo_aliases(struct cli_state *cli) +{ + struct smb_trans2 t2; + uint16 setup = TRANSACT2_SETPATHINFO; + const char *fname = "\\setpathinfo_aliases.txt"; + int fnum; + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_init("findfirst"); + + d_printf("\nChecking for SETPATHINFO aliases\n"); + + t2.in.max_param = 32; + t2.in.max_data = 0x8000; + t2.in.max_setup = 0; + t2.in.flags = 0; + t2.in.timeout = 0; + t2.in.setup_count = 1; + t2.in.setup = &setup; + t2.in.params = data_blob_talloc(mem_ctx, NULL, 4); + t2.in.data = data_blob(NULL, 0); + + cli_unlink(cli, fname); + + fnum = create_complex_file(cli, cli->mem_ctx, fname); + if (fnum == -1) { + printf("ERROR: open of %s failed (%s)\n", fname, cli_errstr(cli)); + } + + cli_write(cli, fnum, 0, (char *)&t2, 0, sizeof(t2)); + cli_close(cli, fnum); + + SSVAL(t2.in.params.data, 2, 0); + + cli_blob_append_string(cli->session, mem_ctx, &t2.in.params, + fname, STR_TERMINATE); + + gen_set_aliases(cli, &t2, 0); + + if (!cli_unlink(cli, fname)) { + printf("unlink: %s\n", cli_errstr(cli)); + } + talloc_destroy(mem_ctx); +} + + +/* look for aliased info levels in trans2 calls */ +BOOL torture_trans2_aliases(int dummy) +{ + struct cli_state *cli; + + if (!torture_open_connection(&cli)) { + return False; + } + + + qfsinfo_aliases(cli); + qfileinfo_aliases(cli); + qpathinfo_aliases(cli); + findfirst_aliases(cli); + setfileinfo_aliases(cli); + setpathinfo_aliases(cli); + + if (!torture_close_connection(cli)) { + return False; + } + + return True; +} diff --git a/source4/torture/cmd_sam.c b/source4/torture/cmd_sam.c new file mode 100644 index 0000000000..3f7f7dfe27 --- /dev/null +++ b/source4/torture/cmd_sam.c @@ -0,0 +1,514 @@ +/* + Unix SMB/CIFS implementation. + SAM module functions + + 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" +#include "samtest.h" + +static void print_account(SAM_ACCOUNT_HANDLE *a) +{ + /* FIXME */ +} + +static NTSTATUS cmd_context(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv) +{ + NTSTATUS status; + char **plugins; + int i; + + plugins = malloc(argc * sizeof(char *)); + + for(i = 1; i < argc; i++) + plugins[i-1] = argv[i]; + + plugins[argc-1] = NULL; + + if(!NT_STATUS_IS_OK(status = make_sam_context_list(&st->context, plugins))) { + printf("make_sam_context_list failed: %s\n", nt_errstr(status)); + SAFE_FREE(plugins); + return status; + } + + SAFE_FREE(plugins); + + return NT_STATUS_OK; +} + +static NTSTATUS cmd_load_module(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv) +{ + char *plugin_arg[2]; + NTSTATUS status; + if (argc != 2 && argc != 3) { + printf("Usage: load [domain-name]\n"); + return NT_STATUS_OK; + } + + if (argc == 3) + asprintf(&plugin_arg[0], "plugin:%s|%s", argv[1], argv[2]); + else + asprintf(&plugin_arg[0], "plugin:%s", argv[1]); + + plugin_arg[1] = NULL; + + if(!NT_STATUS_IS_OK(status = make_sam_context_list(&st->context, plugin_arg))) { + free(plugin_arg[0]); + return status; + } + + free(plugin_arg[0]); + + printf("load: ok\n"); + return NT_STATUS_OK; +} + +static NTSTATUS cmd_get_sec_desc(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS cmd_set_sec_desc(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS cmd_lookup_sid(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv) +{ + char *name; + uint32 type; + NTSTATUS status; + DOM_SID sid; + if (argc != 2) { + printf("Usage: lookup_sid \n"); + return NT_STATUS_INVALID_PARAMETER; + } + + if (!string_to_sid(&sid, argv[1])){ + printf("Unparseable SID specified!\n"); + return NT_STATUS_INVALID_PARAMETER; + } + + if (!NT_STATUS_IS_OK(status = sam_lookup_sid(st->context, st->token, mem_ctx, &sid, &name, &type))) { + printf("sam_lookup_sid failed!\n"); + return status; + } + + printf("Name: %s\n", name); + printf("Type: %d\n", type); /* FIXME: What kind of an integer is type ? */ + + return NT_STATUS_OK; +} + +static NTSTATUS cmd_lookup_name(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv) +{ + DOM_SID sid; + uint32 type; + NTSTATUS status; + if (argc != 3) { + printf("Usage: lookup_name \n"); + return NT_STATUS_INVALID_PARAMETER; + } + + if (!NT_STATUS_IS_OK(status = sam_lookup_name(st->context, st->token, argv[1], argv[2], &sid, &type))) { + printf("sam_lookup_name failed!\n"); + return status; + } + + printf("SID: %s\n", sid_string_static(&sid)); + printf("Type: %d\n", type); + + return NT_STATUS_OK; +} + +static NTSTATUS cmd_lookup_account(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS cmd_lookup_group(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS cmd_lookup_domain(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv) +{ + DOM_SID *sid; + NTSTATUS status; + if (argc != 2) { + printf("Usage: lookup_domain \n"); + return NT_STATUS_INVALID_PARAMETER; + } + + if (!NT_STATUS_IS_OK(status = sam_lookup_domain(st->context, st->token, argv[1], &sid))) { + printf("sam_lookup_name failed!\n"); + return status; + } + + printf("SID: %s\n", sid_string_static(sid)); + + return NT_STATUS_OK; +} + +static NTSTATUS cmd_enum_domains(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv) +{ + int32 domain_count, i; + DOM_SID *domain_sids; + char **domain_names; + NTSTATUS status; + + if (!NT_STATUS_IS_OK(status = sam_enum_domains(st->context, st->token, &domain_count, &domain_sids, &domain_names))) { + printf("sam_enum_domains failed!\n"); + return status; + } + + if (domain_count == 0) { + printf("No domains found!\n"); + return NT_STATUS_OK; + } + + for (i = 0; i < domain_count; i++) { + printf("%s %s\n", domain_names[i], sid_string_static(&domain_sids[i])); + } + + SAFE_FREE(domain_sids); + SAFE_FREE(domain_names); + + return NT_STATUS_OK; +} + +static NTSTATUS cmd_update_domain(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS cmd_show_domain(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv) +{ + NTSTATUS status; + DOM_SID sid; + SAM_DOMAIN_HANDLE *domain; + uint32 tmp_uint32; + uint16 tmp_uint16; + NTTIME tmp_nttime; + BOOL tmp_bool; + const char *tmp_string; + + if (argc != 2) { + printf("Usage: show_domain \n"); + return status; + } + + if (!string_to_sid(&sid, argv[1])){ + printf("Unparseable SID specified!\n"); + return NT_STATUS_INVALID_PARAMETER; + } + + if (!NT_STATUS_IS_OK(status = sam_get_domain_by_sid(st->context, st->token, GENERIC_RIGHTS_DOMAIN_ALL_ACCESS, &sid, &domain))) { + printf("sam_get_domain_by_sid failed\n"); + return status; + } + + if (!NT_STATUS_IS_OK(status = sam_get_domain_num_accounts(domain, &tmp_uint32))) { + printf("sam_get_domain_num_accounts failed: %s\n", nt_errstr(status)); + } else { + printf("Number of accounts: %d\n", tmp_uint32); + } + + if (!NT_STATUS_IS_OK(status = sam_get_domain_num_groups(domain, &tmp_uint32))) { + printf("sam_get_domain_num_groups failed: %s\n", nt_errstr(status)); + } else { + printf("Number of groups: %u\n", tmp_uint32); + } + + if (!NT_STATUS_IS_OK(status = sam_get_domain_num_aliases(domain, &tmp_uint32))) { + printf("sam_get_domain_num_aliases failed: %s\n", nt_errstr(status)); + } else { + printf("Number of aliases: %u\n", tmp_uint32); + } + + if (!NT_STATUS_IS_OK(status = sam_get_domain_name(domain, &tmp_string))) { + printf("sam_get_domain_name failed: %s\n", nt_errstr(status)); + } else { + printf("Domain Name: %s\n", tmp_string); + } + + if (!NT_STATUS_IS_OK(status = sam_get_domain_lockout_count(domain, &tmp_uint16))) { + printf("sam_get_domain_lockout_count failed: %s\n", nt_errstr(status)); + } else { + printf("Lockout Count: %u\n", tmp_uint16); + } + + if (!NT_STATUS_IS_OK(status = sam_get_domain_force_logoff(domain, &tmp_bool))) { + printf("sam_get_domain_force_logoff failed: %s\n", nt_errstr(status)); + } else { + printf("Force Logoff: %s\n", (tmp_bool?"Yes":"No")); + } + + if (!NT_STATUS_IS_OK(status = sam_get_domain_lockout_duration(domain, &tmp_nttime))) { + printf("sam_get_domain_lockout_duration failed: %s\n", nt_errstr(status)); + } else { + printf("Lockout duration: %u\n", tmp_nttime.low); + } + + if (!NT_STATUS_IS_OK(status = sam_get_domain_login_pwdchange(domain, &tmp_bool))) { + printf("sam_get_domain_login_pwdchange failed: %s\n", nt_errstr(status)); + } else { + printf("Password changing allowed: %s\n", (tmp_bool?"Yes":"No")); + } + + if (!NT_STATUS_IS_OK(status = sam_get_domain_max_pwdage(domain, &tmp_nttime))) { + printf("sam_get_domain_max_pwdage failed: %s\n", nt_errstr(status)); + } else { + printf("Maximum password age: %u\n", tmp_nttime.low); + } + + if (!NT_STATUS_IS_OK(status = sam_get_domain_min_pwdage(domain, &tmp_nttime))) { + printf("sam_get_domain_min_pwdage failed: %s\n", nt_errstr(status)); + } else { + printf("Minimal password age: %u\n", tmp_nttime.low); + } + + if (!NT_STATUS_IS_OK(status = sam_get_domain_min_pwdlength(domain, &tmp_uint16))) { + printf("sam_get_domain_min_pwdlength: %s\n", nt_errstr(status)); + } else { + printf("Minimal Password Length: %u\n", tmp_uint16); + } + + if (!NT_STATUS_IS_OK(status = sam_get_domain_pwd_history(domain, &tmp_uint16))) { + printf("sam_get_domain_pwd_history failed: %s\n", nt_errstr(status)); + } else { + printf("Password history: %u\n", tmp_uint16); + } + + if (!NT_STATUS_IS_OK(status = sam_get_domain_reset_count(domain, &tmp_nttime))) { + printf("sam_get_domain_reset_count failed: %s\n", nt_errstr(status)); + } else { + printf("Reset count: %u\n", tmp_nttime.low); + } + + if (!NT_STATUS_IS_OK(status = sam_get_domain_server(domain, &tmp_string))) { + printf("sam_get_domain_server failed: %s\n", nt_errstr(status)); + } else { + printf("Server: %s\n", tmp_string); + } + + return NT_STATUS_OK; +} + +static NTSTATUS cmd_create_account(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS cmd_update_account(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS cmd_delete_account(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS cmd_enum_accounts(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv) +{ + NTSTATUS status; + DOM_SID sid; + int32 account_count, i; + SAM_ACCOUNT_ENUM *accounts; + + if (argc != 2) { + printf("Usage: enum_accounts \n"); + return NT_STATUS_INVALID_PARAMETER; + } + + if (!string_to_sid(&sid, argv[1])){ + printf("Unparseable SID specified!\n"); + return NT_STATUS_INVALID_PARAMETER; + } + + if (!NT_STATUS_IS_OK(status = sam_enum_accounts(st->context, st->token, &sid, 0, &account_count, &accounts))) { + printf("sam_enum_accounts failed: %s\n", nt_errstr(status)); + return status; + } + + if (account_count == 0) { + printf("No accounts found!\n"); + return NT_STATUS_OK; + } + + for (i = 0; i < account_count; i++) + printf("SID: %s\nName: %s\nFullname: %s\nDescription: %s\nACB_BITS: %08X\n\n", + sid_string_static(&accounts[i].sid), accounts[i].account_name, + accounts[i].full_name, accounts[i].account_desc, + accounts[i].acct_ctrl); + + SAFE_FREE(accounts); + + return NT_STATUS_OK; +} + +static NTSTATUS cmd_lookup_account_sid(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv) +{ + NTSTATUS status; + DOM_SID sid; + SAM_ACCOUNT_HANDLE *account; + + if (argc != 2) { + printf("Usage: lookup_account_sid \n"); + return NT_STATUS_INVALID_PARAMETER; + } + + if (!string_to_sid(&sid, argv[1])){ + printf("Unparseable SID specified!\n"); + return NT_STATUS_INVALID_PARAMETER; + } + + if (!NT_STATUS_IS_OK(status = sam_get_account_by_sid(st->context, st->token, GENERIC_RIGHTS_USER_ALL_ACCESS, &sid, &account))) { + printf("context_sam_get_account_by_sid failed: %s\n", nt_errstr(status)); + return status; + } + + print_account(account); + + return NT_STATUS_OK; +} + +static NTSTATUS cmd_lookup_account_name(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv) +{ + NTSTATUS status; + SAM_ACCOUNT_HANDLE *account; + + if (argc != 3) { + printf("Usage: lookup_account_name \n"); + return NT_STATUS_INVALID_PARAMETER; + } + + + if (!NT_STATUS_IS_OK(status = sam_get_account_by_name(st->context, st->token, GENERIC_RIGHTS_USER_ALL_ACCESS, argv[1], argv[2], &account))) { + printf("context_sam_get_account_by_sid failed: %s\n", nt_errstr(status)); + return status; + } + + print_account(account); + + return NT_STATUS_OK; +} + +static NTSTATUS cmd_create_group(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS cmd_update_group(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS cmd_delete_group(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS cmd_enum_groups(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS cmd_lookup_group_sid(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS cmd_lookup_group_name(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS cmd_group_add_member(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +static NTSTATUS cmd_group_del_member(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + + +static NTSTATUS cmd_group_enum(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + + +static NTSTATUS cmd_get_sid_groups(struct samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +struct cmd_set sam_general_commands[] = { + + { "General SAM Commands" }, + + { "load", cmd_load_module, "Load a module", "load [domain-sid]" }, + { "context", cmd_context, "Load specified context", "context [DOMAIN|]backend1[:options] [DOMAIN|]backend2[:options]" }, + { "get_sec_desc", cmd_get_sec_desc, "Get security descriptor info", "get_sec_desc " }, + { "set_sec_desc", cmd_set_sec_desc, "Set security descriptor info", "set_sec_desc " }, + { "lookup_sid", cmd_lookup_sid, "Lookup type of specified SID", "lookup_sid " }, + { "lookup_name", cmd_lookup_name, "Lookup type of specified name", "lookup_name " }, + { NULL } +}; + +struct cmd_set sam_domain_commands[] = { + { "Domain Commands" }, + { "update_domain", cmd_update_domain, "Update domain information", "update_domain [domain-options] domain-name | domain-sid" }, + { "show_domain", cmd_show_domain, "Show domain information", "show_domain domain-sid | domain-name" }, + { "enum_domains", cmd_enum_domains, "Enumerate all domains", "enum_domains " }, + { "lookup_domain", cmd_lookup_domain, "Lookup a domain by name", "lookup_domain domain-name" }, + { NULL } +}; + +struct cmd_set sam_account_commands[] = { + { "Account Commands" }, + { "create_account", cmd_create_account, "Create a new account with specified properties", "create_account [account-options]" }, + { "update_account", cmd_update_account, "Update an existing account", "update_account [account-options] account-sid | account-name" }, + { "delete_account", cmd_delete_account, "Delete an account", "delete_account account-sid | account-name" }, + { "enum_accounts", cmd_enum_accounts, "Enumerate all accounts", "enum_accounts " }, + { "lookup_account", cmd_lookup_account, "Lookup an account by either sid or name", "lookup_account account-sid | account-name" }, + { "lookup_account_sid", cmd_lookup_account_sid, "Lookup an account by sid", "lookup_account_sid account-sid" }, + { "lookup_account_name", cmd_lookup_account_name, "Lookup an account by name", "lookup_account_name account-name" }, + { NULL } +}; + +struct cmd_set sam_group_commands[] = { + { "Group Commands" }, + { "create_group", cmd_create_group, "Create a new group", "create_group [group-opts]" }, + { "update_group", cmd_update_group, "Update an existing group", "update_group [group-opts] group-name | group-sid" }, + { "delete_group", cmd_delete_group, "Delete an existing group", "delete_group group-name | group-sid" }, + { "enum_groups", cmd_enum_groups, "Enumerate all groups", "enum_groups " }, + { "lookup_group", cmd_lookup_group, "Lookup a group by SID or name", "lookup_group group-sid | group-name" }, + { "lookup_group_sid", cmd_lookup_group_sid, "Lookup a group by SID", "lookup_group_sid " }, + { "lookup_group_name", cmd_lookup_group_name, "Lookup a group by name", "lookup_group_name " }, + { "group_add_member", cmd_group_add_member, "Add group member to group", "group_add_member " }, + { "group_del_member", cmd_group_del_member, "Delete group member from group", "group_del_member " }, + { "group_enum", cmd_group_enum, "Enumerate all members of specified group", "group_enum group-sid | group-name" }, + + { "get_sid_groups", cmd_get_sid_groups, "Get a list of groups specified sid is a member of", "group_enum " }, + { NULL } +}; diff --git a/source4/torture/cmd_vfs.c b/source4/torture/cmd_vfs.c new file mode 100644 index 0000000000..b90c53e9fe --- /dev/null +++ b/source4/torture/cmd_vfs.c @@ -0,0 +1,1051 @@ +/* + 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 char *null_string = ""; + +static NTSTATUS cmd_load_module(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, char **argv) +{ + struct smb_vfs_handle_struct *handle; + char *path = lp_vfs_path(0); + char name[PATH_MAX]; + + if (argc != 2) { + printf("Usage: load \n"); + return NT_STATUS_OK; + } + + if (path != NULL && *path != '\0') { + snprintf(name, PATH_MAX, "%s/%s", path, argv[1]); + } else { + snprintf(name, PATH_MAX, "%s", argv[1]); + } + vfs->conn->vfs_private = NULL; + handle = (struct smb_vfs_handle_struct *) smb_xmalloc(sizeof(smb_vfs_handle_struct)); + handle->handle = NULL; + DLIST_ADD(vfs->conn->vfs_private, handle) + if (!vfs_init_custom(vfs->conn, name)) { + DEBUG(0, ("load: error=-1 (vfs_init_custom failed for %s)\n", argv[1])); + 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, char **argv) +{ + char c; + size_t size; + if (argc != 3) { + printf("Usage: populate \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, char **argv) +{ + size_t offset; + size_t len; + if (argc != 1 && argc != 3) { + printf("Usage: showdata [ ]\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, char **argv) +{ + vfs->conn->vfs_ops.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, char **argv) +{ + vfs->conn->vfs_ops.disconnect(vfs->conn); + return NT_STATUS_OK; +} + +static NTSTATUS cmd_disk_free(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, char **argv) +{ + SMB_BIG_UINT diskfree, bsize, dfree, dsize; + if (argc != 2) { + printf("Usage: disk_free \n"); + return NT_STATUS_OK; + } + + diskfree = vfs->conn->vfs_ops.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, char **argv) +{ + if (argc != 2) { + printf("Usage: opendir \n"); + return NT_STATUS_OK; + } + + vfs->currentdir = vfs->conn->vfs_ops.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, char **argv) +{ + struct dirent *dent; + + if (vfs->currentdir == NULL) { + printf("readdir: error=-1 (no open directory)\n"); + return NT_STATUS_UNSUCCESSFUL; + } + + dent = vfs->conn->vfs_ops.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, char **argv) +{ + if (argc != 2) { + printf("Usage: mkdir \n"); + return NT_STATUS_OK; + } + + if (vfs->conn->vfs_ops.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, char **argv) +{ + int ret; + + if (vfs->currentdir == NULL) { + printf("closedir: failure (no directory open)\n"); + return NT_STATUS_UNSUCCESSFUL; + } + + ret = vfs->conn->vfs_ops.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, char **argv) +{ + int flags, fd; + mode_t mode; + char *flagstr; + + mode = 00400; + + if (argc < 3 || argc > 5) { + printf("Usage: open \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 = vfs->conn->vfs_ops.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, char **argv) +{ + int ret = -1; + + if (argc != 2) { + printf("Usage: %s \n", argv[0]); + return NT_STATUS_OK; + } + + if (strcmp("rmdir", argv[0]) == 0 ) { + ret = vfs->conn->vfs_ops.rmdir(vfs->conn, argv[1]); + } else if (strcmp("unlink", argv[0]) == 0 ) { + ret = vfs->conn->vfs_ops.unlink(vfs->conn, argv[1]); + } else if (strcmp("chdir", argv[0]) == 0 ) { + ret = vfs->conn->vfs_ops.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, char **argv) +{ + int fd, ret; + + if (argc != 2) { + printf("Usage: close \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 = vfs->conn->vfs_ops.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, char **argv) +{ + int fd; + size_t size, rsize; + + if (argc != 3) { + printf("Usage: read \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 = vfs->conn->vfs_ops.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, char **argv) +{ + int fd, size, wsize; + + if (argc != 3) { + printf("Usage: write \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 = vfs->conn->vfs_ops.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, char **argv) +{ + int fd, offset, whence; + SMB_OFF_T pos; + + if (argc != 4) { + printf("Usage: lseek \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 = vfs->conn->vfs_ops.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, char **argv) +{ + int ret; + if (argc != 3) { + printf("Usage: rename \n"); + return NT_STATUS_OK; + } + + ret = vfs->conn->vfs_ops.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, char **argv) +{ + int ret, fd; + if (argc != 2) { + printf("Usage: fsync \n"); + return NT_STATUS_OK; + } + + fd = atoi(argv[1]); + ret = vfs->conn->vfs_ops.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, char **argv) +{ + int ret; + char *user; + char *group; + struct passwd *pwd; + struct group *grp; + SMB_STRUCT_STAT st; + + if (argc != 2) { + printf("Usage: stat \n"); + return NT_STATUS_OK; + } + + ret = vfs->conn->vfs_ops.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 = strdup(pwd->pw_name); + else user = null_string; + grp = sys_getgrgid(st.st_gid); + if (grp != NULL) group = strdup(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); + printf(" Blocks: %9u", (unsigned int)st.st_blocks); + printf(" IO Block: %u\n", (unsigned int)st.st_blksize); + 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: %5d/%.16s Gid: %5d/%.16s\n", st.st_uid, user, 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))); + if (user != null_string) SAFE_FREE(user); + if (group!= null_string) SAFE_FREE(group); + return NT_STATUS_OK; +} + + +static NTSTATUS cmd_fstat(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, char **argv) +{ + int fd; + char *user; + char *group; + struct passwd *pwd; + struct group *grp; + SMB_STRUCT_STAT st; + + if (argc != 2) { + printf("Usage: fstat \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 (vfs->conn->vfs_ops.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 = strdup(pwd->pw_name); + else user = null_string; + grp = sys_getgrgid(st.st_gid); + if (grp != NULL) group = strdup(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); + printf(" Blocks: %9u", (unsigned int)st.st_blocks); + printf(" IO Block: %u\n", (unsigned int)st.st_blksize); + 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: %5d/%.16s Gid: %5d/%.16s\n", st.st_uid, user, 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))); + if (user != null_string) SAFE_FREE(user); + if (group!= null_string) SAFE_FREE(group); + return NT_STATUS_OK; +} + + +static NTSTATUS cmd_lstat(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, char **argv) +{ + char *user; + char *group; + struct passwd *pwd; + struct group *grp; + SMB_STRUCT_STAT st; + + if (argc != 2) { + printf("Usage: lstat \n"); + return NT_STATUS_OK; + } + + if (vfs->conn->vfs_ops.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 = strdup(pwd->pw_name); + else user = null_string; + grp = sys_getgrgid(st.st_gid); + if (grp != NULL) group = strdup(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); + printf(" Blocks: %9u", (unsigned int)st.st_blocks); + printf(" IO Block: %u\n", (unsigned int)st.st_blksize); + 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: %5d/%.16s Gid: %5d/%.16s\n", st.st_uid, user, 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))); + if (user != null_string) SAFE_FREE(user); + if (group!= null_string) SAFE_FREE(group); + return NT_STATUS_OK; +} + + +static NTSTATUS cmd_chmod(struct vfs_state *vfs, TALLOC_CTX *mem_ctx, int argc, char **argv) +{ + mode_t mode; + if (argc != 3) { + printf("Usage: chmod \n"); + return NT_STATUS_OK; + } + + mode = atoi(argv[2]); + if (vfs->conn->vfs_ops.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, char **argv) +{ + int fd; + mode_t mode; + if (argc != 3) { + printf("Usage: fchmod \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 (vfs->conn->vfs_ops.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, char **argv) +{ + uid_t uid; + gid_t gid; + if (argc != 4) { + printf("Usage: chown \n"); + return NT_STATUS_OK; + } + + uid = atoi(argv[2]); + gid = atoi(argv[3]); + if (vfs->conn->vfs_ops.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, char **argv) +{ + uid_t uid; + gid_t gid; + int fd; + if (argc != 4) { + printf("Usage: fchown \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 (vfs->conn->vfs_ops.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, char **argv) +{ + char buf[PATH_MAX]; + if (vfs->conn->vfs_ops.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, char **argv) +{ + struct utimbuf times; + if (argc != 4) { + printf("Usage: utime \n"); + return NT_STATUS_OK; + } + times.actime = atoi(argv[2]); + times.modtime = atoi(argv[3]); + if (vfs->conn->vfs_ops.utime(vfs->conn, argv[1], ×) != 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, char **argv) +{ + int fd; + SMB_OFF_T off; + if (argc != 3) { + printf("Usage: ftruncate \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 (vfs->conn->vfs_ops.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, char **argv) +{ + BOOL ret; + int fd; + int op; + long offset; + long count; + int type; + char *typestr; + + if (argc != 6) { + printf("Usage: lock \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 = vfs->conn->vfs_ops.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, char **argv) +{ + if (argc != 3) { + printf("Usage: symlink \n"); + return NT_STATUS_OK; + } + + if (vfs->conn->vfs_ops.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, char **argv) +{ + char buffer[PATH_MAX]; + int size; + + if (argc != 2) { + printf("Usage: readlink \n"); + return NT_STATUS_OK; + } + + if ((size = vfs->conn->vfs_ops.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, char **argv) +{ + if (argc != 3) { + printf("Usage: link \n"); + return NT_STATUS_OK; + } + + if (vfs->conn->vfs_ops.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, char **argv) +{ + mode_t mode; + unsigned int dev_val; + SMB_DEV_T dev; + + if (argc != 4) { + printf("Usage: mknod \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 (vfs->conn->vfs_ops.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, char **argv) +{ + char respath[PATH_MAX]; + + if (argc != 2) { + printf("Usage: realpath \n"); + return NT_STATUS_OK; + } + + if (vfs->conn->vfs_ops.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 " }, + { "populate", cmd_populate, "Populate a data buffer", "populate " }, + { "showdata", cmd_show_data, "Show data currently in data buffer", "show_data [ ]"}, + { "connect", cmd_connect, "VFS connect()", "connect" }, + { "disconnect", cmd_disconnect, "VFS disconnect()", "disconnect" }, + { "disk_free", cmd_disk_free, "VFS disk_free()", "disk_free " }, + { "opendir", cmd_opendir, "VFS opendir()", "opendir " }, + { "readdir", cmd_readdir, "VFS readdir()", "readdir" }, + { "mkdir", cmd_mkdir, "VFS mkdir()", "mkdir " }, + { "rmdir", cmd_pathfunc, "VFS rmdir()", "rmdir " }, + { "closedir", cmd_closedir, "VFS closedir()", "closedir" }, + { "open", cmd_open, "VFS open()", "open " }, + { "close", cmd_close, "VFS close()", "close " }, + { "read", cmd_read, "VFS read()", "read " }, + { "write", cmd_write, "VFS write()", "write " }, + { "lseek", cmd_lseek, "VFS lseek()", "lseek " }, + { "rename", cmd_rename, "VFS rename()", "rename " }, + { "fsync", cmd_fsync, "VFS fsync()", "fsync " }, + { "stat", cmd_stat, "VFS stat()", "stat " }, + { "fstat", cmd_fstat, "VFS fstat()", "fstat " }, + { "lstat", cmd_lstat, "VFS lstat()", "lstat " }, + { "unlink", cmd_pathfunc, "VFS unlink()", "unlink " }, + { "chmod", cmd_chmod, "VFS chmod()", "chmod " }, + { "fchmod", cmd_fchmod, "VFS fchmod()", "fchmod " }, + { "chown", cmd_chown, "VFS chown()", "chown " }, + { "fchown", cmd_fchown, "VFS fchown()", "fchown " }, + { "chdir", cmd_pathfunc, "VFS chdir()", "chdir " }, + { "getwd", cmd_getwd, "VFS getwd()", "getwd" }, + { "utime", cmd_utime, "VFS utime()", "utime " }, + { "ftruncate", cmd_ftruncate, "VFS ftruncate()", "ftruncate " }, + { "lock", cmd_lock, "VFS lock()", "lock " }, + { "symlink", cmd_symlink, "VFS symlink()", "symlink " }, + { "readlink", cmd_readlink, "VFS readlink()", "readlink " }, + { "link", cmd_link, "VFS link()", "link " }, + { "mknod", cmd_mknod, "VFS mknod()", "mknod " }, + { "realpath", cmd_realpath, "VFS realpath()", "realpath " }, + { NULL } +}; diff --git a/source4/torture/denytest.c b/source4/torture/denytest.c new file mode 100644 index 0000000000..ea4b7e5735 --- /dev/null +++ b/source4/torture/denytest.c @@ -0,0 +1,1578 @@ +/* + Unix SMB/CIFS implementation. + SMB torture tester - deny mode scanning functions + Copyright (C) Andrew Tridgell 2001 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 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) +{ + static 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"); + + printf("Testing deny modes with 1 connection\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 %d entries\n", ARRAY_SIZE(denytable1)); + + for (i=0; i + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 DFS_SERVER_COUNT 6 +#define DFS_FILE_COUNT 8 +extern char *host, *share, *password, *username; +static struct cli_client context; +static const char *sockops="TCP_NODELAY"; + +/* + checks for correct DFS cluster support + */ +BOOL torture_dfs_basic(int dummy) +{ + int current_server = 0; + char *fname[DFS_FILE_COUNT]; + int file_server[DFS_FILE_COUNT]; + int fnum[DFS_FILE_COUNT]; + int i; + const char *template = "\\\\%s\\%s\\dfstest%d.tmp"; + char *filedata; + int server_count = 0; + int connection_flags = CLI_FULL_CONNECTION_USE_KERBEROS + | CLI_FULL_CONNECTION_USE_DFS + ; + + printf("starting dfs_basic_test\n"); + cli_client_initialize(&context, sockops, username, password, lp_workgroup(), connection_flags); + + if ((current_server = cli_dfs_open_connection(&context, host, share, connection_flags) < 0)) + return False; + + for (i=0; i < DFS_FILE_COUNT ; i++) { + file_server[i] = 0; + DEBUG(4,("host=%s share=%s cli host=%s cli share=%s\n", + host, share, cli_state_get_host(context.cli[file_server[i]]), + cli_state_get_share(context.cli[file_server[i]]))); + host = cli_state_get_host(context.cli[file_server[i]]); + share = cli_state_get_share(context.cli[file_server[i]]); + asprintf(&fname[i], template, host, share, i); + DEBUG(3,("unlinking %s\n", fname[i])); + cli_nt_unlink(&context, &file_server[i], fname[i], 0); + } + + for (i=0; i < DFS_FILE_COUNT ; i++) { + host = cli_state_get_host(context.cli[file_server[i]]); + share = cli_state_get_share(context.cli[file_server[i]]); + asprintf(&fname[i], template, host, share, i); + DEBUG(3,("open %s on server %s(%d)\n", + fname[i], host, file_server[i])); + fnum[i] = cli_dfs_open(&context, &file_server[i], fname[i], O_RDWR|O_CREAT|O_EXCL, DENY_NONE); + if (fnum[i] == -1) + { + printf("open of %s failed (%s)\n", fname[i], cli_errstr(context.cli[file_server[i]])); + return False; + } + asprintf(&filedata, "%s %d", fname[i], fnum[i]); + DEBUG(3,("write %d bytes (%s) to %s (fid %d) on server %s(%d)\n", + strlen(filedata), filedata, fname[i], fnum[i], + host, file_server[i])); + if (cli_write(context.cli[file_server[i]], fnum[i], 0, filedata, 0, strlen(filedata)) != strlen(filedata)) + { + printf("write failed (%s)\n", cli_errstr(context.cli[file_server[i]])); + return False; + } + + if (!cli_close(context.cli[file_server[i]], fnum[i])) { + printf("close of %s failed (%s)\n", fname[i], cli_errstr(context.cli[file_server[i]])); + return False; + } + } + DEBUG(3,("used Dfs servers:")); + for (i=0; i < DFS_SERVER_COUNT ; i++) { + server_count++; + DEBUG(3,(" %s(%d)", cli_state_get_host(context.cli[file_server[i]]), i)); + if (!torture_close_connection(context.cli[i])) + return False; + } + DEBUG(3,("\n")); + + printf("Passed dfstest, found and used %d Dfs servers\n", server_count); + return True; +} + +/* + Check for correct DFS rename support. + First test is simple rename, a la command line, explorer. + Second test is simulation of MS Word edit/save file. + */ +BOOL torture_dfs_rename(int dummy) +{ + int current_server = -1; + char *fname[DFS_FILE_COUNT]; + int file_server[DFS_FILE_COUNT]; + int fnum[DFS_FILE_COUNT]; + int i; + const char *template = "\\\\%s\\%s\\dfstest%d.tmp"; + const char *template2orig = "\\\\%s\\%s\\dfstestorig.txt"; + const char *template2old = "\\\\%s\\%s\\~dfstestold.txt"; + const char *template2new = "\\\\%s\\%s\\~dfstestnew.txt"; + char *filedata, *newdata; + int server_count = 0; + int connection_flags = CLI_FULL_CONNECTION_USE_KERBEROS + | CLI_FULL_CONNECTION_USE_DFS + ; + + printf("starting dfs_rename_test\n"); + cli_client_initialize(&context, sockops, username, password, + lp_workgroup(), connection_flags); + + if ((current_server = cli_dfs_open_connection(&context, host, share, connection_flags)) < 0) + return False; + + for (i=0; i < DFS_FILE_COUNT ; i++) { + file_server[i] = 0; + slprintf(fname[i],sizeof(fstring)-1,template, host, share, i); + DEBUG(3,("unlinking %s\n", fname[i])); + cli_nt_unlink(&context, &file_server[i], fname[i], 0); + } + /* Simple rename test */ + for (i=0; i < 1 ; i++) { + slprintf(fname[i],sizeof(fstring)-1,template, + cli_state_get_host(context.cli[file_server[i]]), + cli_state_get_share(context.cli[file_server[i]]), i); + DEBUG(3,("open %s on server %s(%d)\n", + fname[i], cli_state_get_host(context.cli[file_server[i]]), file_server[i])); + + fnum[i] = cli_dfs_open(&context, &file_server[i], fname[i], O_RDWR|O_CREAT|O_EXCL, DENY_NONE); + if (fnum[i] == -1) { + printf("open of %s failed (%s)\n", fname[i], cli_errstr(context.cli[file_server[i]])); + return False; + } + asprintf(&filedata, "%s %d", fname[i], (int)getpid()); + DEBUG(3,("write %d bytes (%s) to %s (fid %d) on server %s(%d)\n", + strlen(filedata), filedata, fname[i], fnum[i], + cli_state_get_host(context.cli[file_server[i]]), file_server[i])); + if (cli_write(context.cli[file_server[i]], fnum[i], 0, filedata, 0, strlen(filedata)) != strlen(filedata)) + { + printf("write failed (%s)\n", cli_errstr(context.cli[file_server[i]])); + return False; + } + + if (!cli_close(context.cli[file_server[i]], fnum[i])) { + printf("close of %s failed (%s)\n", fname[i], cli_errstr(context.cli[file_server[i]])); + return False; + } + } + // now attempt to rename the file + DEBUG(3,("rename %s to %s on server %s(%d)\n", + fname[0], fname[1], cli_state_get_host(context.cli[file_server[i]]), file_server[0])); + if (!cli_dfs_rename(&context, &file_server[0], fname[0], fname[1])) { + printf("rename of %s to %s failed (%s)\n", fname[0], fname[1], cli_errstr(context.cli[file_server[0]])); + return False; + } + // clean up + DEBUG(3,("used Dfs servers:")); + for (i=0; i < DFS_SERVER_COUNT ; i++) { + server_count++; + DEBUG(3,(" %s(%d)", cli_state_get_host(context.cli[file_server[i]]), i)); + if (!torture_close_connection(context.cli[i])) + return False; + } + DEBUG(3,("\n")); + printf("Dfstest: passed simple rename test\n"); + + /* Now try more complicated test, a la MS Word. + * Open existing file (x) and read file and close. + * Then open, write to new temp name file (~x.new), close. + * Then rename old file name to old temp name file (~x.old). + * Then rename new temp name file to oroginal name (x). */ + cli_client_initialize(&context, sockops, username, password, + lp_workgroup(), connection_flags); + + if ((current_server = cli_dfs_open_connection(&context, host, share, connection_flags)) < 0) + return False; + slprintf(fname[0],sizeof(fname[0])-1,template2orig, host, share); + slprintf(fname[1],sizeof(fname[1])-1,template2old, host, share); + slprintf(fname[2],sizeof(fname[2])-1,template2new, host, share); + for (i=0; i < DFS_FILE_COUNT ; i++) { + file_server[i] = 0; + fnum[i] = 0; + DEBUG(3,("unlinking %s\n", fname[i])); + cli_nt_unlink(&context, &file_server[i], fname[i], 0); + } + asprintf(&fname[0],template2orig, + cli_state_get_host(context.cli[0]), + cli_state_get_share(context.cli[0]), 0); + asprintf(&fname[1],template2old, + cli_state_get_host(context.cli[1]), + cli_state_get_share(context.cli[1]), 1); + asprintf(&fname[2],template2new, + cli_state_get_host(context.cli[2]), + cli_state_get_share(context.cli[2]), 2); + DEBUG(3,("edit(MS Word) %s on server %s(%d)\n", + fname[0], cli_state_get_host(context.cli[0]), file_server[0])); + DEBUG(3,("open %s on server %s(%d)\n", + fname[0], cli_state_get_host(context.cli[0]), file_server[0])); + + fnum[0] = cli_dfs_open(&context, &file_server[0], fname[0], O_RDWR|O_CREAT|O_EXCL, DENY_NONE); + if (fnum[0] == -1) + { + printf("open of %s failed (%s)\n", fname[0], cli_errstr(context.cli[file_server[0]])); + return False; + } + slprintf(filedata, sizeof(fstring)-1, "%s %d", fname[0], (int)getpid()); + DEBUG(3,("write %d bytes (%s) to %s (fid %d) on server %s(%d)\n", + strlen(filedata), filedata, fname[0], fnum[0], + cli_state_get_host(context.cli[0]), file_server[0])); + if (cli_write(context.cli[file_server[0]], fnum[0], 0, filedata, 0, strlen(filedata)) != strlen(filedata)) + { + printf("write failed (%s)\n", cli_errstr(context.cli[file_server[0]])); + return False; + } + // read data from original file + DEBUG(3,("read %s (fid %d) on server %s(%d)\n", + fname[0], fnum[0], cli_state_get_host(context.cli[0]), file_server[0])); + if (cli_read(context.cli[file_server[0]], fnum[0], filedata, 0, strlen(filedata)) != strlen(filedata)) + { + printf("read failed (%s)", cli_errstr(context.cli[file_server[0]])); + return False; + } + DEBUG(3,("close %s on server %s(%d)\n", + fname[0], cli_state_get_host(context.cli[0]), file_server[0])); + if (!cli_close(context.cli[file_server[0]], fnum[0])) { + printf("close of %s failed (%s)\n", fname[0], cli_errstr(context.cli[file_server[0]])); + return False; + } + // open new temp file, write data + DEBUG(3,("open %s on server %s(%d)\n", + fname[2], cli_state_get_host(context.cli[2]), file_server[2])); + fnum[2] = cli_dfs_open(&context, &file_server[2], fname[2], O_RDWR|O_CREAT|O_EXCL, DENY_NONE); + if (fnum[2] == -1) + { + printf("open of %s failed (%s)\n", fname[2], cli_errstr(context.cli[file_server[2]])); + return False; + } + DEBUG(3,("write %d bytes (%s) to %s (fid %d) on server %s(%d)\n", + strlen(filedata), filedata, fname[2], fnum[2], + cli_state_get_host(context.cli[2]), file_server[2])); + if (cli_write(context.cli[file_server[2]], fnum[2], 0, filedata, 0, strlen(filedata)) != strlen(filedata)) + { + printf("write failed (%s)\n", cli_errstr(context.cli[file_server[2]])); + return False; + } + slprintf(newdata, sizeof(fstring)-1, "new data: %s %d", fname[0], (int)getpid()); + DEBUG(3,("write new data %d bytes (%s) to %s (fid %d) on server %s(%d)\n", + strlen(newdata), newdata, fname[2], fnum[2], + cli_state_get_host(context.cli[2]), file_server[2])); + if (cli_write(context.cli[file_server[2]], fnum[2], 0, newdata, strlen(filedata), strlen(newdata)) != strlen(newdata)) + { + printf("write failed (%s)\n", cli_errstr(context.cli[file_server[2]])); + return False; + } + DEBUG(3,("close %s on server %s(%d)\n", + fname[2], cli_state_get_host(context.cli[2]), file_server[2])); + if (!cli_close(context.cli[file_server[2]], fnum[2])) { + printf("close of %s failed (%s)\n", fname[2], cli_errstr(context.cli[file_server[2]])); + return False; + } + DEBUG(3,("close successful %s on server %s(%d)\n", + fname[2], cli_state_get_host(context.cli[2]), file_server[2])); + // rename original file to temp + DEBUG(4,("file_server[0]=%d\n", file_server[0])); + DEBUG(4,("context.cli[file_server[0]].desthost=%s\n", cli_state_get_host(context.cli[0]))); + DEBUG(3,("rename %s to %s on server %s(%d)\n", + fname[0], fname[1], cli_state_get_host(context.cli[0]), file_server[0])); + if (!cli_dfs_rename(&context, &file_server[0], fname[0], fname[1])) { + printf("rename of %s to %s failed (%s)\n", fname[0], fname[1], cli_errstr(context.cli[file_server[0]])); + return False; + } + // name new temp file to original + DEBUG(3,("rename %s to %s on server %s(%d)\n", + fname[2], fname[0], cli_state_get_host(context.cli[2]), file_server[2])); + if (!cli_dfs_rename(&context, &file_server[2], fname[2], fname[0])) { + printf("rename of %s to %s failed (%s)\n", fname[2], fname[0], cli_errstr(context.cli[file_server[2]])); + return False; + } + printf("Dfstest: passed MS Word rename test\n"); + // clean up + DEBUG(3,("used Dfs servers:")); + for (i=0; i < DFS_SERVER_COUNT ; i++) { + server_count++; + DEBUG(3,(" %s(%d)", cli_state_get_host(context.cli[i]), i)); + if (!torture_close_connection(context.cli[i])) + return False; + } + DEBUG(3,("\n")); + + printf("Passed dfs_rename_test\n"); + return True; +} +struct list_fn_parms { + struct cli_client *context; + char* rname; +} list_fn_parms; + +void dfs_list_fn(file_info *finfo, const char *rname, void* parmsp); +void delete_file(file_info *finfo, const char *rname) +{ + int server = 0; + char *fname; + + DEBUG(3,("deleting file %s in %s\n", finfo->name, rname)); + asprintf(&fname, "%s\\%s", rname, finfo->name); + cli_nt_unlink(&context, &server, fname, 0); +} +void delete_directory(file_info *finfo, const char *rname) +{ + int server = 0; + char *dname, *rname2; + + DEBUG(3,("deleting directory %s in %s\n", finfo->name, rname)); + asprintf(&dname, "%s%s\\*", rname, finfo->name); + cli_nt_unlink(&context, &server, dname, 0); + asprintf(&dname, "%s%s\\*", rname, finfo->name); + asprintf(&rname2, "%s%s", rname, finfo->name); + cli_search(context.cli[0], dname, FILE_ATTRIBUTE_DIRECTORY, + dfs_list_fn, (void*)rname2); + cli_dfs_rmdir(&context, &server, rname2); +} + +void dfs_list_fn(file_info *finfo, const char *name, void* parmsp) +{ + struct list_fn_parms *parms = (struct list_fn_parms*)parmsp; + + DEBUG(4,("processing %s in %s\n", finfo->name, parms->rname)); + if (finfo->mode & FILE_ATTRIBUTE_DIRECTORY) { + delete_directory(finfo, parms->rname); + } + else { + delete_file(finfo, parms->rname); + } +} + +/* + checks for correct DFS cluster support creating random dirs/files. + */ +#define DFS_RANDOM_FILE_COUNT 10 +#define DFS_RANDOM_DIR_COUNT 3 +#define DFS_RANDOM_DIR_LEVELS 2 +BOOL torture_dfs_random(int dummy) +{ + char *fname[DFS_RANDOM_FILE_COUNT]; + int file_server[DFS_RANDOM_FILE_COUNT]; + char *dname[DFS_RANDOM_DIR_COUNT]; + int dir_server[DFS_RANDOM_DIR_COUNT]; + char *rname; + int fnum[DFS_FILE_COUNT]; + int i; + const char *ftemplate = "%s\\dfsfile%d.tmp"; + const char *alltemplate = "\\\\%s\\%s\\dfs*.tmp"; + char *filedata; + int server_count = 0; + int file_count; + int connection_flags = CLI_FULL_CONNECTION_USE_KERBEROS + | CLI_FULL_CONNECTION_USE_DFS + ; + + printf("starting dfs_random_test\n"); + cli_client_initialize(&context, sockops, username, password, + lp_workgroup(), connection_flags); + + if ((dir_server[0] = cli_dfs_open_connection(&context, host, share, connection_flags)) < 0) + return False; + + // get list of directories named dfsdir*. + // delete all files in these directories using wild card, + // then delete directory. + asprintf(&rname, "\\\\%s\\%s\\", + cli_state_get_host(context.cli[0]), + cli_state_get_share(context.cli[0])); + asprintf(&fname[0], alltemplate, + cli_state_get_host(context.cli[0]), + cli_state_get_share(context.cli[0])); + DEBUG(3,("deleting files %s in %s on server %s(%d)\n", + fname[0], rname, cli_state_get_host(context.cli[0]), dir_server[0])); + file_count = cli_search(context.cli[0], fname[0], FILE_ATTRIBUTE_DIRECTORY, dfs_list_fn, (void*)rname); + + // create random directory names with 0-n levels + asprintf(&dname[0], "\\\\%s\\%s\\", + cli_state_get_host(context.cli[0]), + cli_state_get_share(context.cli[0])); + DEBUG(3,("creating directories in %s on server %s(%d)\n", + rname, cli_state_get_host(context.cli[0]), dir_server[0])); + for (i=1; i < DFS_RANDOM_DIR_COUNT; i++) { + dir_server[i] = 0; + asprintf(&dname[i], + "\\\\%s\\%s\\dfsdir%d.tmp", + cli_state_get_host(context.cli[dir_server[i]]), + cli_state_get_share(context.cli[dir_server[i]]), + (int)sys_random()%10000); + DEBUG(3,("mkdir %s on server %s(%d)\n", + dname[i], cli_state_get_host(context.cli[dir_server[i]]), dir_server[i])); + if (!cli_dfs_mkdir(&context, &dir_server[i], dname[i])) { + printf("mkdir of %s failed (%s)\n", dname[i], cli_errstr(context.cli[dir_server[i]])); + return False; + } + } + + for (i=0; i < DFS_RANDOM_FILE_COUNT ; i++) { + // select a directory randomly, create a file in it. + int dn = (int)sys_random()%DFS_RANDOM_DIR_COUNT; + file_server[i] = dir_server[dn]; + asprintf(&fname[i], ftemplate, dname[dn], i); + DEBUG(3,("open %s on server %s(%d)\n", + fname[i], cli_state_get_host(context.cli[dir_server[i]]), file_server[i])); + fnum[i] = cli_dfs_open(&context, &file_server[i], fname[i], O_RDWR|O_CREAT|O_EXCL, DENY_NONE); + if (fnum[i] == -1) + { + printf("open of %s failed (%s)\n", fname[i], cli_errstr(context.cli[file_server[i]])); + return False; + } + + asprintf(&filedata, "%s %d", fname[i], fnum[i]); + DEBUG(3,("write %d bytes (%s) to %s (fid %d) on server %s(%d)\n", + strlen(filedata), filedata, fname[i], fnum[i], + cli_state_get_host(context.cli[dir_server[i]]), file_server[i])); + if (cli_write(context.cli[file_server[i]], fnum[i], 0, filedata, 0, strlen(filedata)) != strlen(filedata)) + { + printf("write failed (%s)\n", cli_errstr(context.cli[file_server[i]])); + return False; + } + + if (!cli_close(context.cli[file_server[i]], fnum[i])) { + printf("close of %s failed (%s)\n", fname[i], cli_errstr(context.cli[file_server[i]])); + return False; + } + } + DEBUG(3,("used Dfs servers:")); + for (i=0; i < DFS_SERVER_COUNT ; i++) { + server_count++; + DEBUG(3,(" %s(%d)", cli_state_get_host(context.cli[i]), i)); + if (!torture_close_connection(context.cli[i])) + return False; + } + DEBUG(3,("\n")); + + printf("Passed dfs_random_test\n"); + return True; +} diff --git a/source4/torture/genbit.c b/source4/torture/genbit.c new file mode 100644 index 0000000000..6afde37706 --- /dev/null +++ b/source4/torture/genbit.c @@ -0,0 +1,83 @@ +/* + Unix SMB/CIFS implementation. + Gentest test definitions + + Copyright (C) James Myers 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +get_field_function test_field_get_file_attr; +get_field_function test_field_get_fid; +get_field_function test_field_get_filename; +get_field_function test_field_get_mtime; +get_field_function test_field_get_trans2; +get_field_function test_field_get_fsinfo_level; + +static struct unlink_test_parms_t gen_unlink_test_parms; +static struct close_test_parms_t gen_close_test_parms; +static struct qfsi_test_parms_t gen_qfsi_test_parms; + +static struct trans2_parms trans2_qfsi_parms = { + testFieldTypeTrans2, 1, 2, 0, 0, TRANSACT2_QFSINFO +}; + +static struct field_test_spec gen_unlink_test_spec[] = { + {"FATTR", testFieldTypeFileAttr, NULL, + 1, test_field_get_file_attr}, + {"FNAME", testFieldTypeFilename, NULL, + -1, test_field_get_filename}, + {"", -1, NULL, -1, NULL} +}; + +static struct field_test_spec gen_close_test_spec[] = { + {"FID", testFieldTypeFid, NULL, 1, + test_field_get_fid}, + {"MTIME", testFieldTypeMtime, NULL, 2, + test_field_get_mtime}, + {"", -1, NULL, -1, NULL} +}; + +static struct field_test_spec gen_qfsi_test_spec[] = { + {"TRANS2", testFieldTypeTrans2, + (void*)&trans2_qfsi_parms, 15, + test_field_get_trans2}, + {"INFO_LEVEL", 0, NULL, 1, test_field_get_fsinfo_level}, + {"", -1, NULL, -1, NULL} +}; + +static struct enum_test gen_enum_tests[] = { + {SMBunlink, "UNLINK", TEST_COND_TCON, + testTypeFilename, + TEST_OPTION_FILE_EXISTS | + TEST_OPTION_FILE_SYSTEM | + TEST_OPTION_FILE_HIDDEN | + TEST_OPTION_FILE_INVISIBLE | + TEST_OPTION_FILE_WILDCARD | + TEST_OPTION_FILE_NOT_EXIST, + 1, gen_unlink_test_spec, (void*)&gen_unlink_test_parms, + gen_execute_unlink, gen_verify_unlink}, + {SMBclose, "CLOSE", TEST_COND_TCON, + testTypeFid, + TEST_OPTION_FID_VALID | TEST_OPTION_FID_INVALID, + 3, gen_close_test_spec, (void*)&gen_close_test_parms, + gen_execute_close, gen_verify_close}, + {SMBtrans2, "QUERY_FS_INFO", TEST_COND_TCON, + testTypeConnected, + 1, + 16, gen_qfsi_test_spec, (void*)&gen_qfsi_test_parms, + gen_execute_qfsi, gen_verify_qfsi}, + {-1, NULL, 0, 0, 0, -1, NULL, NULL, NULL} +}; diff --git a/source4/torture/gendefs.h b/source4/torture/gendefs.h new file mode 100644 index 0000000000..6afde37706 --- /dev/null +++ b/source4/torture/gendefs.h @@ -0,0 +1,83 @@ +/* + Unix SMB/CIFS implementation. + Gentest test definitions + + Copyright (C) James Myers 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +get_field_function test_field_get_file_attr; +get_field_function test_field_get_fid; +get_field_function test_field_get_filename; +get_field_function test_field_get_mtime; +get_field_function test_field_get_trans2; +get_field_function test_field_get_fsinfo_level; + +static struct unlink_test_parms_t gen_unlink_test_parms; +static struct close_test_parms_t gen_close_test_parms; +static struct qfsi_test_parms_t gen_qfsi_test_parms; + +static struct trans2_parms trans2_qfsi_parms = { + testFieldTypeTrans2, 1, 2, 0, 0, TRANSACT2_QFSINFO +}; + +static struct field_test_spec gen_unlink_test_spec[] = { + {"FATTR", testFieldTypeFileAttr, NULL, + 1, test_field_get_file_attr}, + {"FNAME", testFieldTypeFilename, NULL, + -1, test_field_get_filename}, + {"", -1, NULL, -1, NULL} +}; + +static struct field_test_spec gen_close_test_spec[] = { + {"FID", testFieldTypeFid, NULL, 1, + test_field_get_fid}, + {"MTIME", testFieldTypeMtime, NULL, 2, + test_field_get_mtime}, + {"", -1, NULL, -1, NULL} +}; + +static struct field_test_spec gen_qfsi_test_spec[] = { + {"TRANS2", testFieldTypeTrans2, + (void*)&trans2_qfsi_parms, 15, + test_field_get_trans2}, + {"INFO_LEVEL", 0, NULL, 1, test_field_get_fsinfo_level}, + {"", -1, NULL, -1, NULL} +}; + +static struct enum_test gen_enum_tests[] = { + {SMBunlink, "UNLINK", TEST_COND_TCON, + testTypeFilename, + TEST_OPTION_FILE_EXISTS | + TEST_OPTION_FILE_SYSTEM | + TEST_OPTION_FILE_HIDDEN | + TEST_OPTION_FILE_INVISIBLE | + TEST_OPTION_FILE_WILDCARD | + TEST_OPTION_FILE_NOT_EXIST, + 1, gen_unlink_test_spec, (void*)&gen_unlink_test_parms, + gen_execute_unlink, gen_verify_unlink}, + {SMBclose, "CLOSE", TEST_COND_TCON, + testTypeFid, + TEST_OPTION_FID_VALID | TEST_OPTION_FID_INVALID, + 3, gen_close_test_spec, (void*)&gen_close_test_parms, + gen_execute_close, gen_verify_close}, + {SMBtrans2, "QUERY_FS_INFO", TEST_COND_TCON, + testTypeConnected, + 1, + 16, gen_qfsi_test_spec, (void*)&gen_qfsi_test_parms, + gen_execute_qfsi, gen_verify_qfsi}, + {-1, NULL, 0, 0, 0, -1, NULL, NULL, NULL} +}; diff --git a/source4/torture/genparm.c b/source4/torture/genparm.c new file mode 100644 index 0000000000..4d968ba6c3 --- /dev/null +++ b/source4/torture/genparm.c @@ -0,0 +1,732 @@ +/* + Unix SMB/CIFS implementation. + SMB test generator - load and parse test config + Copyright (C) James Myers 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 "gentest.h" + +static struct gentest_context_t *contextP; + +#define NUMPARAMETERS (sizeof(parm_table) / sizeof(struct parm_struct)) + +static BOOL do_parameter(const char *pszParmName, const char *pszParmValue); +static BOOL do_section(const char *pszSectionName); + +/* prototypes for the special type handlers */ +static BOOL handle_tests(const char *pszParmValue, char **ptr); + +static BOOL handle_options(const char *pszParmValue, char **ptr); +static BOOL handle_fields(const char *pszParmValue, char **ptr); + +static struct enum_list enum_command[] = { + { + SMBunlink, "SMBunlink" + }, + {SMBclose, "SMBclosex"}, + {-1, NULL} + }; +static struct enum_list enum_condition[] = { + { + TEST_COND_NEGPROT, "TEST_COND_NEGPROT" + }, + {TEST_COND_SESSION, "TEST_COND_SESSION"}, + {TEST_COND_TCON, "TEST_COND_TCON"}, + {TEST_COND_FID, "TEST_COND_FID"}, + {-1, NULL} + }; +static struct enum_list enum_test_type[] = { + { + testTypeConnected, "Connected" + }, + {testTypeFilename, "Filename"}, + {testTypeFid, "FID"}, + {-1, NULL} + }; +static struct enum_list enum_options[] = { + {TEST_OPTION_FILE_EXISTS, "FILE_EXISTS"}, + {TEST_OPTION_FILE_NOT_EXIST, "FILE_NOT_EXIST"}, + {TEST_OPTION_FILE_HIDDEN, "FILE_HIDDEN"}, + {TEST_OPTION_FILE_SYSTEM, "FILE_SYSTEM"}, + {TEST_OPTION_FILE_INVISIBLE, "FILE_INVISIBLE"}, + {TEST_OPTION_FILE_WILDCARD, "FILE_WILDCARD"}, + {TEST_OPTION_FID_INVALID, "FID_INVALID"}, + {TEST_OPTION_FID_VALID, "FID_VALID"}, + {-1, NULL} + }; +static struct enum_list enum_execute[] = { + {(int)gen_execute_unlink, "gen_execute_unlink"}, + {(int)gen_execute_close, "gen_execute_close"}, + {-1, NULL} + }; +static struct enum_list enum_verify[] = { + { + (int)gen_verify_unlink, "gen_verify_unlink" + }, + {(int)gen_verify_close, "gen_verify_close"}, + {-1, NULL} + }; +static struct enum_list enum_field_type[] = { + { + testFieldTypeFilename, "Filename" + }, + {testFieldTypeFileAttr, "FileAttr"}, + {testFieldTypeFid, "FID"}, + {testFieldTypeMtime, "Mtime"}, + {testFieldTypeTrans2, "Trans2"}, + {-1, NULL} + }; +static struct enum_list enum_function[] = { + { + (int)test_field_get_filename, "test_field_get_filename" + }, + {(int)test_field_get_file_attr, "test_field_get_file_attr"}, + {-1, NULL} + }; + +/* Note: We do not initialise the defaults union - it is not allowed in ANSI C + */ +#define GEN_FLAG_GLOBAL 0x0001 /* fundamental options */ +#define GEN_FLAG_TEST 0x0002 /* test options */ +#define GEN_FLAG_FIELD 0x0004 /* field options */ + +static struct { + int command; + char *name; + int debug; + int condition; + int type; + int options; + int words; + struct field_test_spec* fields; + int field_count; + void* execute; + void* verify; +} +test_section; + +static struct { + char *name; + int type; + BOOL random; + int words; + void * function; +} +field_section; + +static struct parm_struct parm_table[] = { + {"Base Options", P_SEP, P_SEPARATOR + }, + /* global section parameters */ + {"tests", P_LIST, P_GLOBAL, NULL, handle_tests, NULL, GEN_FLAG_GLOBAL}, + + /* test section parameters */ + {"Test section", P_SEP, P_SEPARATOR}, + {"command", P_ENUM, P_LOCAL, &test_section.command, NULL, enum_command, GEN_FLAG_TEST}, + {"name", P_STRING, P_LOCAL, &test_section.name, NULL, NULL, GEN_FLAG_TEST}, + {"debug", P_INTEGER, P_LOCAL, &test_section.debug, NULL, NULL, GEN_FLAG_TEST}, + {"condition", P_ENUM, P_LOCAL, &test_section.condition, NULL, enum_condition, GEN_FLAG_TEST}, + {"type", P_ENUM, P_LOCAL, &test_section.type, NULL, enum_test_type, GEN_FLAG_TEST}, + {"options", P_LIST, P_LOCAL, &test_section.options, handle_options, NULL, GEN_FLAG_TEST}, + {"word count", P_INTEGER, P_LOCAL, &test_section.words, NULL, NULL, GEN_FLAG_TEST}, + {"fields", P_LIST, P_LOCAL, NULL, handle_fields, NULL, GEN_FLAG_TEST}, + {"execute", P_ENUM, P_LOCAL, &test_section.execute, NULL, enum_execute, GEN_FLAG_TEST}, + {"verify", P_ENUM, P_LOCAL, &test_section.verify, NULL, enum_verify, GEN_FLAG_TEST}, + + /* field section parameters */ + {"Field section", P_SEP, P_SEPARATOR}, + {"type", P_ENUM, P_LOCAL, &field_section.type, NULL, enum_field_type, GEN_FLAG_FIELD}, + {"random", P_BOOL, P_LOCAL, &field_section.random, NULL, NULL, GEN_FLAG_FIELD}, + {"word count", P_INTEGER, P_LOCAL, &field_section.words, NULL, NULL, GEN_FLAG_FIELD}, + {"function", P_ENUM, P_LOCAL, &field_section.function, NULL, enum_function, GEN_FLAG_FIELD}, + + {NULL, P_BOOL, P_NONE, NULL, NULL, NULL, 0} + }; + +static BOOL handle_tests(const char *pszParmValue, char **ptr) { + contextP->testNames = str_list_make(pszParmValue, NULL); + return True; +} +static BOOL handle_options(const char *pszParmValue, char **ptr) { + /* convert option names (in enum_options) to flags */ + char **str_array; + + str_array = str_list_make(pszParmValue, NULL); + + if (str_array) { + size_t i, j; + for ( j = 0; str_array[j] != NULL; j++) { + BOOL optionValid = False; + for (i = 0; enum_options[i].name; i++) { + if (strequal(str_array[j], + enum_options[i].name)) { + *(int *)ptr |= enum_options[i].value; + optionValid = True; + break; + } + } + if (!optionValid) + DEBUG(0,("handle_options: '%s' invalid option\n", + str_array[j])); + } + } + DEBUG(9,("handle_options: %s -> %p\n", pszParmValue, *ptr)); + + return True; +} + +static BOOL handle_fields(const char *pszParmValue, char **ptr) { + /* create initialized field structures for each name */ + char **str_array; + + str_array = str_list_make(pszParmValue, NULL); + + if (str_array) { + size_t i; + for ( i = 0; str_array[i] != NULL; i++) + test_section.field_count++; + /* allocate new field array */ + test_section.fields = talloc(contextP->mem_ctx, + test_section.field_count * sizeof(struct field_test_spec)); + for ( i = 0; str_array[i] != NULL; i++) + test_section.fields[i].name = str_array[i]; + } + 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 section) { + int iIndex; + unsigned validFlags = 0; + + if (*pszParmName == '-') + return (-1); + + /* Check for section-specific parameters. + * This allows the same parameter name to be used in + * different sections with different meanings. + */ + if (section == GEN_SECTION_GLOBAL) + validFlags |= GEN_FLAG_GLOBAL; + if (section == GEN_SECTION_TEST) + validFlags |= GEN_FLAG_TEST; + if (section == GEN_SECTION_FIELD) + validFlags |= GEN_FLAG_FIELD; + for (iIndex = 0; parm_table[iIndex].label; iIndex++) + if ((parm_table[iIndex].flags & validFlags) && + 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); +} + +/*************************************************************************** + Process a parameter +***************************************************************************/ + +static BOOL gen_do_parm(struct gentest_context_t *context, + const char *pszParmName, const char *pszParmValue) { + int parmnum, i; + void *parm_ptr = NULL; /* where we are going to store the result */ + void *def_ptr = NULL; + + parmnum = map_parameter(pszParmName, context->iCurrentSectionType); + + if (parmnum < 0) { + DEBUG(0, ("Ignoring unknown parameter \"%s\"\n", pszParmName)); + return (True); + } + DEBUG(19,("gen_do_parm: parm %s is valid\n", pszParmName)); + def_ptr = parm_table[parmnum].ptr; + + /* we might point at a test, a field or a global */ + if (context->iCurrentSectionType == GEN_SECTION_GLOBAL) { + 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 = def_ptr; + } + + /* if it is a special case then go ahead */ + if (parm_table[parmnum].special) { + parm_table[parmnum].special(pszParmValue, (char **)parm_ptr); + return (True); + } + DEBUG(19,("gen_do_parm: parm %s type=%d\n", pszParmName, + parm_table[parmnum].type)); + + /* 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_INTEGER: + *(int *)parm_ptr = atoi(pszParmValue); + break; + + case P_LIST: + *(char ***)parm_ptr = str_list_make(pszParmValue, NULL); + break; + + case P_STRING: + parm_ptr = talloc_strdup(context->mem_ctx, pszParmValue); + 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; + default: + break; + } + + return (True); +} +/*************************************************************************** + Process a parameter. +***************************************************************************/ + +static BOOL do_parameter(const char *pszParmName, const char *pszParmValue) { + BOOL bRetval; + + DEBUG(4, ("doing parameter %s = %s\n", pszParmName, pszParmValue)); + bRetval = gen_do_parm(contextP, pszParmName, pszParmValue); + + return bRetval; +} + +/*************************************************************************** +Check a test for consistency. Return False if the test is in any way +incomplete or faulty, else True. +***************************************************************************/ + +static BOOL test_ok(struct gentest_context_t *context,int iTest) { + BOOL bRetval = True; + + DEBUG(9,("test_ok: index=%d, tests@%p\n", iTest, + context->tests)); + /* initialize new test section */ + DEBUG(9,("test_ok: name=%s\n", test_section.name)); + context->tests[iTest].name = test_section.name; + context->tests[iTest].debug = test_section.debug; + context->tests[iTest].type = test_section.type; + context->tests[iTest].command = test_section.command; + context->tests[iTest].initial_conditions = test_section.condition; + context->tests[iTest].options = test_section.options; + context->tests[iTest].word_count = test_section.words; + context->tests[iTest].fields = test_section.fields; + context->tests[iTest].field_count = test_section.field_count; + context->tests[iTest].execute = test_section.execute; + context->tests[iTest].verify = test_section.verify; + + /* validate test entry */ + DEBUG(9,("test_ok: validate name=%s\n", test_section.name)); + if (context->tests[iTest].name[0] == '\0') { + DEBUG(0, ("The following message indicates an internal error:\n")); + DEBUG(0, ("No test name in test entry.\n")); + bRetval = False; + } + if (bRetval) { + context->tests[iTest].valid = True; + DEBUG(9,("added valid test %s\n",test_section.name)); + } + + return (bRetval); +} +/*************************************************************************** +Check a field for consistency. Return False if the field is in any way +incomplete or faulty, else True. +***************************************************************************/ + +static BOOL field_ok(struct gentest_context_t *context,int iField) { + BOOL bRetval = True; + + /* setup new field entry */ + DEBUG(9,("field_ok: index=%d, fields@%p\n", iField, + context->fields)); + context->fields[iField].name = field_section.name; + context->fields[iField].type = field_section.type; + context->fields[iField].random = field_section.random; + context->fields[iField].word_count = field_section.words; + context->fields[iField].function = field_section.function; + + /* validate field */ + if (context->fields[iField].name[0] == '\0') { + DEBUG(0, ("The following message indicates an internal error:\n")); + DEBUG(0, ("No field name in field entry.\n")); + bRetval = False; + } + if (bRetval) { + context->fields[iField].valid = True; + DEBUG(9,("added valid field %s\n",field_section.name)); + } + + return (bRetval); +} +/*************************************************************************** +Find a test by name. Otherwise works like get_test. +***************************************************************************/ + +static int gettestbyname(struct gentest_context_t *context, + const char *pszTestName) { + int iTest; + + for (iTest = context->iNumTests - 1; iTest >= 0; iTest--) + if (context->tests[iTest].valid && + strwicmp(context->tests[iTest].name, pszTestName) == 0) { + break; + } + + return (iTest); +} +/*************************************************************************** +Find a field by name. Otherwise works like get_field. +***************************************************************************/ + +static int getfieldbyname(struct gentest_context_t *context, + const char *pszFieldName) { + int iField; + + for (iField = context->iNumFields - 1; iField >= 0; iField--) + if (context->fields[iField].valid && + strwicmp(context->fields[iField].name, pszFieldName) == 0) { + break; + } + + return (iField); +} +/*************************************************************************** + Add a new test to the tests array initialising it with the given + test. +***************************************************************************/ + +static int add_a_test(struct gentest_context_t *context, + const char *name) { + int i; + int num_to_alloc = context->iNumTests + 1; + + DEBUG(3, ("add_a_test: %s at index %d\n", name, num_to_alloc-1)); + /* it might already exist */ + if (name) { + i = gettestbyname(context, name); + if (i >= 0) + return (i); + } + + /* find an invalid one */ + for (i = 0; i < context->iNumTests; i++) + if (!context->tests[i].valid) + break; + + /* if not, then create one */ + DEBUG(3, ("add_a_test: add %s at index %d\n", name, i)); + if (i == context->iNumTests) { + struct enum_test *tsp; + + tsp = talloc_realloc(context->mem_ctx, context->tests, + sizeof(struct enum_test) * + num_to_alloc); + + if (!tsp) { + DEBUG(0,("add_a_test: failed to enlarge TestPtrs!\n")); + return (-1); + } else { + context->tests = tsp; + } + + context->iNumTests++; + DEBUG(3, ("add_a_test: tests@%p\n", tsp)); + } //else + //free_test(context->tests[i]); + /* reinitialize test section fields */ + test_section.command = 0; + test_section.name = talloc_strdup(context->mem_ctx, name); + test_section.debug = 0; + test_section.condition = 0; + test_section.type = 0; + test_section.options = 0; + test_section.words = 0; + test_section.fields = NULL; + test_section.field_count = 0; + test_section.execute = NULL; + test_section.verify = NULL; + context->tests[i].valid = False; + + if (name) + context->tests[i].name = test_section.name; + DEBUG(3, ("add_a_test: added %s at index %d\n", name, i)); + return (i); +} +/*************************************************************************** + Add a new field to the fields array initialising it with the given + field. +***************************************************************************/ + +static int add_a_field(struct gentest_context_t *context, + const char *name) { + int i; + int num_to_alloc = context->iNumFields + 1; + + DEBUG(3, ("add_a_field: %s at index %d\n", name, num_to_alloc-1)); + /* it might already exist */ + if (name) { + i = getfieldbyname(context, name); + if (i >= 0) + return (i); + } + + /* find an invalid one */ + for (i = 0; i < context->iNumFields; i++) + if (!context->fields[i].valid) + break; + + /* if not, then create one */ + DEBUG(3, ("add_a_field: add %s at index %d\n", name, i)); + if (i == context->iNumFields) { + field_test_spec *tsp; + + tsp = talloc_realloc(context->mem_ctx, context->fields, + sizeof(field_test_spec) * + num_to_alloc); + + if (!tsp) { + DEBUG(0,("add_a_field: failed to enlarge FieldPtrs!\n")); + return (-1); + } else { + context->fields = tsp; + } + + context->iNumFields++; + DEBUG(3, ("add_a_field: fields@%p\n", tsp)); + } + + /* reinitialize field section fields */ + field_section.name = NULL; + field_section.type = 0; + field_section.random = False; + field_section.words = 0; + field_section.function = NULL; + context->fields[i].valid = False; + + if (name) + field_section.name = talloc_strdup(context->mem_ctx, name); + DEBUG(3, ("add_a_field: added %s at index %d\n", name, i)); + return (i); +} +/*************************************************************************** + Process a new section (test or field). + Returns True on success, False on failure. +***************************************************************************/ + +static BOOL do_section(const char *pszSectionName) { + BOOL bRetval; + BOOL isglobal = (strwicmp(pszSectionName, GLOBAL_NAME) == 0); + char *sectionType, *sectionName, *p; + + bRetval = False; + DEBUG(4, ("doing section %s\n", pszSectionName)); + /* if we've just struck a global section, note the fact. */ + contextP->bInGlobalSection = isglobal; + + /* check for multiple global sections */ + if (contextP->bInGlobalSection) { + DEBUG(3, ("Processing section \"[%s]\"\n", pszSectionName)); + contextP->iCurrentSectionType = GEN_SECTION_GLOBAL; + return (True); + } else if (contextP->iCurrentSectionType == GEN_SECTION_GLOBAL) { + /* just finished global section */ + ; + } + + /* parse section name (form */ + sectionType = talloc_strdup(contextP->mem_ctx, pszSectionName); + p = strchr_m(sectionType,':'); + if (p) { + *p = 0; + sectionName = talloc_strdup(contextP->mem_ctx, p+1); + } else { + DEBUG(0, ("Invalid section name %s\n", pszSectionName)); + return False; + } + + /* if we have a current test or field, tidy it up before moving on */ + bRetval = True; + + if (contextP->iTestIndex >= 0 && contextP->iCurrentSectionType == GEN_SECTION_TEST) + bRetval = test_ok(contextP, contextP->iTestIndex); + if (contextP->iFieldIndex >= 0 && contextP->iCurrentSectionType == GEN_SECTION_FIELD) + bRetval = field_ok(contextP, contextP->iFieldIndex); + + /* determine type of this section */ + contextP->iCurrentSectionType = GEN_SECTION_INVALID; + if (strequal(sectionType, "test")) + contextP->iCurrentSectionType = GEN_SECTION_TEST; + if (strequal(sectionType, "field")) + contextP->iCurrentSectionType = GEN_SECTION_FIELD; + if (contextP->iCurrentSectionType == GEN_SECTION_INVALID) { + DEBUG(0, ("Invalid section type %s\n", sectionType)); + return False; + } + + /* if all is still well, move to the next record in the tests 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 (contextP->iCurrentSectionType == GEN_SECTION_TEST) { + if ((contextP->iTestIndex = add_a_test(contextP, sectionName)) + < 0) { + DEBUG(0, ("Failed to add a new test\n")); + return (False); + } + } + if (contextP->iCurrentSectionType == GEN_SECTION_FIELD) { + if ((contextP->iFieldIndex = add_a_field(contextP, sectionName)) + < 0) { + DEBUG(0, ("Failed to add a new field\n")); + return (False); + } + } + } + + return (bRetval); +} + +/*************************************************************************** + Load the test configuration from the test config file. Return True on success, + False on failure. +***************************************************************************/ + +BOOL gen_load_config(struct gentest_context_t *contextPTR) { + char *n2; + BOOL bRetval; + + contextP = contextPTR; + contextP->param_opt = NULL; + + n2 = talloc_strdup(contextP->mem_ctx, contextP->config_filename); + + /* We get sections first, so have to start 'behind' to make up */ + contextP->iTestIndex = -1; + bRetval = pm_process(n2, do_section, do_parameter); + + /* finish up the last section */ + DEBUG(4, ("pm_process() returned %s\n", BOOLSTR(bRetval))); + + /* if we have a current test or field, tidy it up before moving on */ + if (contextP->iTestIndex >= 0 && contextP->iCurrentSectionType == GEN_SECTION_TEST) + bRetval = test_ok(contextP, contextP->iTestIndex); + if (contextP->iFieldIndex >= 0 && contextP->iCurrentSectionType == GEN_SECTION_FIELD) + bRetval = field_ok(contextP, contextP->iFieldIndex); + + /* OK, we've parsed the configuration, now we need to match + * the field sections to fields required by tests */ + if (bRetval) { + int i,j,k; + BOOL fieldValid; + for (i=0; iiNumTests; i++) { + DEBUG(19,("gen_load_config: process test %d %s\n", + i, contextP->tests[i].name)); + for (j=0; jtests[i].field_count; j++) { + fieldValid = False; + DEBUG(19,("gen_load_config: look for field %s\n", + contextP->tests[i].fields[j].name)); + for (k=0; kiNumFields; k++) { + DEBUG(19,("gen_load_config: compare field %s\n", + contextP->fields[k].name)); + if (strequal(contextP->tests[i].fields[j].name, + contextP->fields[k].name)) { + /* matching field found */ + fieldValid = True; + contextP->tests[i].fields[j].type = contextP->fields[k].type; + contextP->tests[i].fields[j].word_count = contextP->fields[k].word_count; + contextP->tests[i].fields[j].function = contextP->fields[k].function; + contextP->tests[i].fields[j].valid = contextP->fields[k].valid; + contextP->tests[i].fields[j].random = contextP->fields[k].random; + contextP->tests[i].fields[j].parms = contextP->fields[k].parms; + break; + } + if (fieldValid) + break; + } + if (!fieldValid) { + contextP->tests[i].fields[j].valid = False; + contextP->tests[i].fields[j].function = test_field_get_null; + DEBUG(0,("missing field section: %s\n", + contextP->tests[i].fields[j].name)); + } + } + } + } + + return (bRetval); +} diff --git a/source4/torture/gentest.c b/source4/torture/gentest.c new file mode 100644 index 0000000000..abe6d057c3 --- /dev/null +++ b/source4/torture/gentest.c @@ -0,0 +1,2113 @@ +/* + Unix SMB/CIFS implementation. + generic testing tool + 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" + +#define NSERVERS 2 +#define NINSTANCES 2 + +/* global options */ +static struct gentest_options { + BOOL showall; + BOOL analyze; + BOOL analyze_always; + BOOL analyze_continuous; + uint_t max_open_handles; + uint_t seed; + uint_t numops; + BOOL use_oplocks; + char **ignore_patterns; + const char *seeds_file; + BOOL use_preset_seeds; + BOOL fast_reconnect; +} options; + +/* mapping between open handles on the server and local handles */ +static struct { + BOOL active; + uint_t instance; + uint_t server_fnum[NSERVERS]; + const char *name; +} *open_handles; +static uint_t num_open_handles; + +/* state information for the servers. We open NINSTANCES connections to + each server */ +static struct { + struct cli_state *cli[NINSTANCES]; + char *server_name; + char *share_name; + char *username; + char *password; +} servers[NSERVERS]; + +/* the seeds and flags for each operation */ +static struct { + uint_t seed; + BOOL disabled; +} *op_parms; + + +/* oplock break info */ +static struct { + BOOL got_break; + uint16 fnum; + uint16 handle; + uint8 level; + BOOL do_close; +} oplocks[NSERVERS][NINSTANCES]; + +/* change notify reply info */ +static struct { + int notify_count; + NTSTATUS status; + struct smb_notify notify; +} notifies[NSERVERS][NINSTANCES]; + +/* info relevant to the current operation */ +static struct { + const char *name; + uint_t seed; + NTSTATUS status; + uint_t opnum; + TALLOC_CTX *mem_ctx; +} current_op; + + + +#define BAD_HANDLE 0xFFFE + +static BOOL oplock_handler(struct cli_transport *transport, uint16 tid, uint16 fnum, uint8 level, void *private); +static void idle_func(struct cli_transport *transport, void *private); + +/* + check if a string should be ignored. This is used as the basis + for all error ignore settings +*/ +static BOOL ignore_pattern(const char *str) +{ + int i; + if (!options.ignore_patterns) return False; + + for (i=0;options.ignore_patterns[i];i++) { + if (strcmp(options.ignore_patterns[i], str) == 0 || + gen_fnmatch(options.ignore_patterns[i], str) == 0) { + DEBUG(2,("Ignoring '%s'\n", str)); + return True; + } + } + return False; +} + +/***************************************************** +connect to the servers +*******************************************************/ +static BOOL connect_servers_fast(void) +{ + int h, i; + + /* close all open files */ + for (h=0;htransport, oplock_handler, NULL); + cli_transport_idle_handler(servers[i].cli[j]->transport, idle_func, 10, NULL); + } + } + + return True; +} + +/* + work out the time skew between the servers - be conservative +*/ +static uint_t time_skew(void) +{ + uint_t ret; + ret = ABS(servers[0].cli[0]->transport->negotiate.server_time - + servers[1].cli[0]->transport->negotiate.server_time); + return ret + 300; +} + +/* + turn an fnum for an instance into a handle +*/ +static uint_t fnum_to_handle(int server, int instance, uint16 fnum) +{ + uint_t i; + for (i=0;i 0 && count++ < 10*options.max_open_handles) { + h = random() % options.max_open_handles; + if (open_handles[h].active && + open_handles[h].instance == instance) { + return h; + } + } + return BAD_HANDLE; +} + +/* + return a file handle, but skewed so we don't close the last + couple of handles too readily +*/ +static uint16 gen_fnum_close(int instance) +{ + if (num_open_handles < 3) { + if (gen_chance(80)) return BAD_HANDLE; + } + + return gen_fnum(instance); +} + +/* + generate an integer in a specified range +*/ +static int gen_int_range(uint_t min, uint_t max) +{ + uint_t r = random(); + return min + (r % (1+max-min)); +} + +/* + return a fnum for use as a root fid + be careful to call GEN_SET_FNUM() when you use this! +*/ +static uint16 gen_root_fid(int instance) +{ + if (gen_chance(5)) return gen_fnum(instance); + return 0; +} + +/* + generate a file offset +*/ +static int gen_offset(void) +{ + if (gen_chance(20)) return 0; + return gen_int_range(0, 1024*1024); +} + +/* + generate a io count +*/ +static int gen_io_count(void) +{ + if (gen_chance(20)) return 0; + return gen_int_range(0, 4096); +} + +/* + generate a filename +*/ +static const char *gen_fname(void) +{ + const char *names[] = {"\\gentest\\gentest.dat", + "\\gentest\\foo", + "\\gentest\\foo2.sym", + "\\gentest\\foo3.dll", + "\\gentest\\foo4", + "\\gentest\\foo4:teststream1", + "\\gentest\\foo4:teststream2", + "\\gentest\\foo5.exe", + "\\gentest\\foo5.exe:teststream3", + "\\gentest\\foo5.exe:teststream4", + "\\gentest\\foo6.com", + "\\gentest\\blah", + "\\gentest\\blah\\blergh.txt", + "\\gentest\\blah\\blergh2", + "\\gentest\\blah\\blergh3.txt", + "\\gentest\\blah\\blergh4", + "\\gentest\\blah\\blergh5.txt", + "\\gentest\\blah\\blergh5", + "\\gentest\\blah\\.", +#if 0 + /* this causes problem with w2k3 */ + "\\gentest\\blah\\..", +#endif + "\\gentest\\a_very_long_name.bin", + "\\gentest\\x.y", + "\\gentest\\blah"}; + int i; + + do { + i = gen_int_range(0, ARRAY_SIZE(names)-1); + } while (ignore_pattern(names[i])); + + return names[i]; +} + +/* + generate a filename with a higher chance of choosing an already + open file +*/ +static const char *gen_fname_open(int instance) +{ + uint16 h; + h = gen_fnum(instance); + if (h == BAD_HANDLE) { + return gen_fname(); + } + return open_handles[h].name; +} + +/* + generate a wildcard pattern +*/ +static const char *gen_pattern(void) +{ + int i; + const char *names[] = {"\\gentest\\*.dat", + "\\gentest\\*", + "\\gentest\\*.*", + "\\gentest\\blah\\*.*", + "\\gentest\\blah\\*", + "\\gentest\\?"}; + + if (gen_chance(50)) return gen_fname(); + + do { + i = gen_int_range(0, ARRAY_SIZE(names)-1); + } while (ignore_pattern(names[i])); + + return names[i]; +} + +/* + generate a bitmask +*/ +static uint32 gen_bits_mask(uint_t mask) +{ + uint_t ret = random(); + return ret & mask; +} + +/* + generate a bitmask with high probability of the first mask + and low of the second +*/ +static uint32 gen_bits_mask2(uint32 mask1, uint32 mask2) +{ + if (gen_chance(10)) return gen_bits_mask(mask2); + return gen_bits_mask(mask1); +} + +/* + generate a boolean +*/ +static BOOL gen_bool(void) +{ + return gen_bits_mask2(0x1, 0xFF); +} + +/* + return a lockingx lock mode +*/ +static uint16 gen_lock_mode(void) +{ + if (gen_chance(5)) return gen_bits_mask(0xFFFF); + if (gen_chance(20)) return gen_bits_mask(0x1F); + return gen_bits_mask(LOCKING_ANDX_SHARED_LOCK | LOCKING_ANDX_LARGE_FILES); +} + +/* + generate a pid +*/ +static uint16 gen_pid(void) +{ + if (gen_chance(10)) return gen_bits_mask(0xFFFF); + return getpid(); +} + +/* + generate a lock count +*/ +static SMB_OFF_T gen_lock_count(void) +{ + return gen_int_range(0, 3); +} + +/* + generate a ntcreatex flags field +*/ +static uint32 gen_ntcreatex_flags(void) +{ + if (gen_chance(70)) return NTCREATEX_FLAGS_EXTENDED; + return gen_bits_mask2(0x1F, 0xFFFFFFFF); +} + +/* + generate a NT access mask +*/ +static uint32 gen_access_mask(void) +{ + if (gen_chance(50)) return SEC_RIGHT_MAXIMUM_ALLOWED; + if (gen_chance(20)) return GENERIC_RIGHTS_FILE_ALL_ACCESS; + return gen_bits_mask(0xFFFFFFFF); +} + +/* + generate a ntcreatex create options bitfield +*/ +static uint32 gen_create_options(void) +{ + if (gen_chance(20)) return gen_bits_mask(0xFFFFFFFF); + if (gen_chance(50)) return 0; + return gen_bits_mask(NTCREATEX_OPTIONS_DELETE_ON_CLOSE | NTCREATEX_OPTIONS_DIRECTORY); +} + +/* + generate a ntcreatex open disposition +*/ +static uint32 gen_open_disp(void) +{ + if (gen_chance(10)) return gen_bits_mask(0xFFFFFFFF); + return gen_int_range(0, 5); +} + +/* + generate an openx open mode +*/ +static uint16 gen_openx_mode(void) +{ + if (gen_chance(20)) return gen_bits_mask(0xFFFF); + if (gen_chance(20)) return gen_bits_mask(0xFF); + return OPENX_MODE_DENY_NONE | gen_bits_mask(0x3); +} + +/* + generate an openx flags field +*/ +static uint16 gen_openx_flags(void) +{ + if (gen_chance(20)) return gen_bits_mask(0xFFFF); + return gen_bits_mask(0x7); +} + +/* + generate an openx open function +*/ +static uint16 gen_openx_func(void) +{ + if (gen_chance(20)) return gen_bits_mask(0xFFFF); + return gen_bits_mask(0x13); +} + +/* + generate a file attrib combination +*/ +static uint32 gen_attrib(void) +{ + if (gen_chance(20)) return gen_bits_mask(0xFFFFFFFF); + return gen_bits_mask(FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_DIRECTORY); +} + +/* + generate a unix timestamp +*/ +static time_t gen_timet(void) +{ + if (gen_chance(30)) return 0; + return (time_t)random(); +} + +/* + generate a unix timestamp +*/ +static NTTIME gen_nttime(void) +{ + NTTIME ret; + unix_to_nt_time(&ret, gen_timet()); + return ret; +} + +/* + generate a milliseconds protocol timeout +*/ +static uint32 gen_timeout(void) +{ + if (gen_chance(98)) return 0; + return random() % 50; +} + +/* + generate a file allocation size +*/ +static uint_t gen_alloc_size(void) +{ + uint_t ret; + + if (gen_chance(30)) return 0; + + ret = random() % 4*1024*1024; + /* give a high chance of a round number */ + if (gen_chance(60)) { + ret &= ~(1024*1024 - 1); + } + return ret; +} + +/* + generate an ea_struct +*/ +struct ea_struct gen_ea_struct(void) +{ + struct ea_struct ea; + const char *names[] = {"EAONE", + "", + "FOO!", + " WITH SPACES ", + ".", + "AVERYLONGATTRIBUTENAME"}; + const char *values[] = {"VALUE1", + "", + "NOT MUCH FOO", + " LEADING SPACES ", + ":", + "ASOMEWHATLONGERATTRIBUTEVALUE"}; + int i; + + do { + i = gen_int_range(0, ARRAY_SIZE(names)-1); + } while (ignore_pattern(names[i])); + + ea.name.s = names[i]; + + do { + i = gen_int_range(0, ARRAY_SIZE(values)-1); + } while (ignore_pattern(values[i])); + + ea.value = data_blob(values[i], strlen(values[i])); + + if (gen_chance(10)) ea.flags = gen_bits_mask(0xFF); + ea.flags = 0; + + return ea; +} + + +/* + this is called when a change notify reply comes in +*/ +static void async_notify(struct cli_request *req) +{ + struct smb_notify notify; + NTSTATUS status; + int i, j; + uint16 tid; + struct cli_transport *transport = req->transport; + + tid = SVAL(req->in.hdr, HDR_TID); + + status = smb_raw_changenotify_recv(req, current_op.mem_ctx, ¬ify); + if (NT_STATUS_IS_OK(status)) { + printf("notify tid=%d num_changes=%d action=%d name=%s\n", + tid, + notify.out.num_changes, + notify.out.changes[0].action, + notify.out.changes[0].name.s); + } + + for (i=0;itransport && + tid == servers[i].cli[j]->tree->tid) { + notifies[i][j].notify_count++; + notifies[i][j].status = status; + notifies[i][j].notify = notify; + } + } + } +} + +/* + the oplock handler will either ack the break or close the file +*/ +static BOOL oplock_handler(struct cli_transport *transport, uint16 tid, uint16 fnum, uint8 level, void *private) +{ + union smb_close io; + NTSTATUS status; + int i, j; + BOOL do_close; + struct cli_tree *tree = NULL; + + srandom(current_op.seed); + do_close = gen_chance(50); + + for (i=0;itransport && + tid == servers[i].cli[j]->tree->tid) { + oplocks[i][j].got_break = True; + oplocks[i][j].fnum = fnum; + oplocks[i][j].handle = fnum_to_handle(i, j, fnum); + oplocks[i][j].level = level; + oplocks[i][j].do_close = do_close; + tree = servers[i].cli[j]->tree; + } + } + } + + if (!tree) { + printf("Oplock break not for one of our trees!?\n"); + return False; + } + + if (!do_close) { + printf("oplock ack fnum=%d\n", fnum); + return cli_oplock_ack(tree, fnum, level == 1? 0x102 : 2); + } + + printf("oplock close fnum=%d\n", fnum); + + io.close.level = RAW_CLOSE_CLOSE; + io.close.in.fnum = fnum; + io.close.in.write_time = 0; + status = smb_raw_close(tree, &io); + + if (!NT_STATUS_IS_OK(status)) { + printf("WARNING: close failed in oplock_handler_close - %s\n", nt_errstr(status)); + } + return True; +} + + +/* + the idle function tries to cope with getting an oplock break on a connection, and + an operation on another connection blocking until that break is acked + we check for operations on all transports in the idle function +*/ +static void idle_func(struct cli_transport *transport, void *private) +{ + int i, j; + for (i=0;itransport && + cli_transport_pending(servers[i].cli[j]->transport)) { + if (!cli_request_receive_next(servers[i].cli[j]->transport)) { + printf("Connection to server %d instance %d died!\n", + i, j); + exit(1); + } + } + } + } + +} + + +/* + compare NTSTATUS, using checking ignored patterns +*/ +static BOOL compare_status(NTSTATUS status1, NTSTATUS status2) +{ + if (NT_STATUS_EQUAL(status1, status2)) return True; + + /* one code being an error and the other OK is always an error */ + if (NT_STATUS_IS_OK(status1) || NT_STATUS_IS_OK(status2)) return False; + + /* if we are ignoring one of the status codes then consider this a match */ + if (ignore_pattern(nt_errstr(status1)) || + ignore_pattern(nt_errstr(status2))) { + return True; + } + return False; +} + + +/* + check for pending packets on all connections +*/ +static void check_pending(void) +{ + int i, j; + + msleep(20); + + for (j=0;jtransport)) { + if (!cli_request_receive_next(servers[i].cli[j]->transport)) { + printf("Connection to server %d instance %d died!\n", + i, j); + exit(1); + } + } + } + } +} + +/* + check that the same oplock breaks have been received by all instances +*/ +static BOOL check_oplocks(const char *call) +{ + int i, j; + int tries = 0; + +again: + check_pending(); + + for (j=0;jtree; \ + status[i] = call; \ + } \ + current_op.status = status[0]; \ + for (i=1;i time_skew() && \ + !ignore_pattern(#field)) { \ + printf("Mismatch in %s - 0x%x 0x%x\n", #field, \ + (int)parm[0].field, (int)parm[1].field); \ + return False; \ + } \ +} while(0) + +#define CHECK_NTTIMES_EQUAL(field) do { \ + if (ABS(nt_time_to_unix(&parm[0].field) - \ + nt_time_to_unix(&parm[1].field)) > time_skew() && \ + !ignore_pattern(#field)) { \ + printf("Mismatch in %s - 0x%x 0x%x\n", #field, \ + (int)nt_time_to_unix(&parm[0].field), \ + (int)nt_time_to_unix(&parm[1].field)); \ + return False; \ + } \ +} while(0) + +/* + generate openx operations +*/ +static BOOL handler_openx(int instance) +{ + union smb_open parm[NSERVERS]; + NTSTATUS status[NSERVERS]; + + parm[0].openx.level = RAW_OPEN_OPENX; + parm[0].openx.in.flags = gen_openx_flags(); + parm[0].openx.in.open_mode = gen_openx_mode(); + parm[0].openx.in.search_attrs = gen_attrib(); + parm[0].openx.in.file_attrs = gen_attrib(); + parm[0].openx.in.write_time = gen_timet(); + parm[0].openx.in.open_func = gen_openx_func(); + parm[0].openx.in.size = gen_io_count(); + parm[0].openx.in.timeout = gen_timeout(); + parm[0].openx.in.fname = gen_fname_open(instance); + + if (!options.use_oplocks) { + /* mask out oplocks */ + parm[0].openx.in.flags &= ~(OPENX_FLAGS_REQUEST_OPLOCK| + OPENX_FLAGS_REQUEST_BATCH_OPLOCK); + } + + GEN_COPY_PARM; + GEN_CALL(smb_raw_open(tree, current_op.mem_ctx, &parm[i])); + + CHECK_EQUAL(openx.out.attrib); + CHECK_EQUAL(openx.out.size); + CHECK_EQUAL(openx.out.access); + CHECK_EQUAL(openx.out.ftype); + CHECK_EQUAL(openx.out.devstate); + CHECK_EQUAL(openx.out.action); + CHECK_EQUAL(openx.out.access_mask); + CHECK_EQUAL(openx.out.unknown); + CHECK_TIMES_EQUAL(openx.out.write_time); + + /* open creates a new file handle */ + ADD_HANDLE(parm[0].openx.in.fname, openx.out.fnum); + + return True; +} + + +/* + generate ntcreatex operations +*/ +static BOOL handler_ntcreatex(int instance) +{ + union smb_open parm[NSERVERS]; + NTSTATUS status[NSERVERS]; + + parm[0].ntcreatex.level = RAW_OPEN_NTCREATEX; + parm[0].ntcreatex.in.flags = gen_ntcreatex_flags(); + parm[0].ntcreatex.in.root_fid = gen_root_fid(instance); + parm[0].ntcreatex.in.access_mask = gen_access_mask(); + parm[0].ntcreatex.in.alloc_size = gen_alloc_size(); + parm[0].ntcreatex.in.file_attr = gen_attrib(); + parm[0].ntcreatex.in.share_access = gen_bits_mask2(0x7, 0xFFFFFFFF); + parm[0].ntcreatex.in.open_disposition = gen_open_disp(); + parm[0].ntcreatex.in.create_options = gen_create_options(); + parm[0].ntcreatex.in.impersonation = gen_bits_mask2(0, 0xFFFFFFFF); + parm[0].ntcreatex.in.security_flags = gen_bits_mask2(0, 0xFF); + parm[0].ntcreatex.in.fname = gen_fname_open(instance); + + if (!options.use_oplocks) { + /* mask out oplocks */ + parm[0].ntcreatex.in.flags &= ~(NTCREATEX_FLAGS_REQUEST_OPLOCK| + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK); + } + + GEN_COPY_PARM; + if (parm[0].ntcreatex.in.root_fid != 0) { + GEN_SET_FNUM(ntcreatex.in.root_fid); + } + GEN_CALL(smb_raw_open(tree, current_op.mem_ctx, &parm[i])); + + CHECK_EQUAL(ntcreatex.out.oplock_level); + CHECK_EQUAL(ntcreatex.out.create_action); + CHECK_NTTIMES_EQUAL(ntcreatex.out.create_time); + CHECK_NTTIMES_EQUAL(ntcreatex.out.access_time); + CHECK_NTTIMES_EQUAL(ntcreatex.out.write_time); + CHECK_NTTIMES_EQUAL(ntcreatex.out.change_time); + CHECK_EQUAL(ntcreatex.out.attrib); + CHECK_EQUAL(ntcreatex.out.alloc_size); + CHECK_EQUAL(ntcreatex.out.size); + CHECK_EQUAL(ntcreatex.out.file_type); + CHECK_EQUAL(ntcreatex.out.ipc_state); + CHECK_EQUAL(ntcreatex.out.is_directory); + + /* ntcreatex creates a new file handle */ + ADD_HANDLE(parm[0].ntcreatex.in.fname, ntcreatex.out.fnum); + + return True; +} + +/* + generate close operations +*/ +static BOOL handler_close(int instance) +{ + union smb_close parm[NSERVERS]; + NTSTATUS status[NSERVERS]; + + parm[0].close.level = RAW_CLOSE_CLOSE; + parm[0].close.in.fnum = gen_fnum_close(instance); + parm[0].close.in.write_time = gen_timet(); + + GEN_COPY_PARM; + GEN_SET_FNUM(close.in.fnum); + GEN_CALL(smb_raw_close(tree, &parm[i])); + + REMOVE_HANDLE(close.in.fnum); + + return True; +} + +/* + generate unlink operations +*/ +static BOOL handler_unlink(int instance) +{ + struct smb_unlink parm[NSERVERS]; + NTSTATUS status[NSERVERS]; + + parm[0].in.pattern = gen_pattern(); + parm[0].in.attrib = gen_attrib(); + + GEN_COPY_PARM; + GEN_CALL(smb_raw_unlink(tree, &parm[i])); + + return True; +} + +/* + generate chkpath operations +*/ +static BOOL handler_chkpath(int instance) +{ + struct smb_chkpath parm[NSERVERS]; + NTSTATUS status[NSERVERS]; + + parm[0].in.path = gen_fname_open(instance); + + GEN_COPY_PARM; + GEN_CALL(smb_raw_chkpath(tree, &parm[i])); + + return True; +} + +/* + generate mkdir operations +*/ +static BOOL handler_mkdir(int instance) +{ + union smb_mkdir parm[NSERVERS]; + NTSTATUS status[NSERVERS]; + + parm[0].mkdir.level = RAW_MKDIR_MKDIR; + parm[0].mkdir.in.path = gen_fname_open(instance); + + GEN_COPY_PARM; + GEN_CALL(smb_raw_mkdir(tree, &parm[i])); + + return True; +} + +/* + generate rmdir operations +*/ +static BOOL handler_rmdir(int instance) +{ + struct smb_rmdir parm[NSERVERS]; + NTSTATUS status[NSERVERS]; + + parm[0].in.path = gen_fname_open(instance); + + GEN_COPY_PARM; + GEN_CALL(smb_raw_rmdir(tree, &parm[i])); + + return True; +} + +/* + generate rename operations +*/ +static BOOL handler_rename(int instance) +{ + struct smb_rename parm[NSERVERS]; + NTSTATUS status[NSERVERS]; + + parm[0].in.pattern1 = gen_pattern(); + parm[0].in.pattern2 = gen_pattern(); + parm[0].in.attrib = gen_attrib(); + + GEN_COPY_PARM; + GEN_CALL(smb_raw_rename(tree, &parm[i])); + + return True; +} + + +/* + generate readx operations +*/ +static BOOL handler_readx(int instance) +{ + union smb_read parm[NSERVERS]; + NTSTATUS status[NSERVERS]; + + parm[0].readx.level = RAW_READ_READX; + parm[0].readx.in.fnum = gen_fnum(instance); + parm[0].readx.in.offset = gen_offset(); + parm[0].readx.in.mincnt = gen_io_count(); + parm[0].readx.in.maxcnt = gen_io_count(); + parm[0].readx.in.remaining = gen_io_count(); + parm[0].readx.out.data = talloc(current_op.mem_ctx, + MAX(parm[0].readx.in.mincnt, parm[0].readx.in.maxcnt)); + + GEN_COPY_PARM; + GEN_SET_FNUM(readx.in.fnum); + GEN_CALL(smb_raw_read(tree, &parm[i])); + + CHECK_EQUAL(readx.out.remaining); + CHECK_EQUAL(readx.out.compaction_mode); + CHECK_EQUAL(readx.out.nread); + + return True; +} + +/* + generate writex operations +*/ +static BOOL handler_writex(int instance) +{ + union smb_write parm[NSERVERS]; + NTSTATUS status[NSERVERS]; + + parm[0].writex.level = RAW_WRITE_WRITEX; + parm[0].writex.in.fnum = gen_fnum(instance); + parm[0].writex.in.offset = gen_offset(); + parm[0].writex.in.wmode = gen_bits_mask(0xFFFF); + parm[0].writex.in.remaining = gen_io_count(); + parm[0].writex.in.count = gen_io_count(); + parm[0].writex.in.data = talloc_zero(current_op.mem_ctx, parm[0].writex.in.count); + + GEN_COPY_PARM; + GEN_SET_FNUM(writex.in.fnum); + GEN_CALL(smb_raw_write(tree, &parm[i])); + + CHECK_EQUAL(writex.out.nwritten); + CHECK_EQUAL(writex.out.remaining); + + return True; +} + +/* + generate lockingx operations +*/ +static BOOL handler_lockingx(int instance) +{ + union smb_lock parm[NSERVERS]; + NTSTATUS status[NSERVERS]; + int n, nlocks; + + parm[0].lockx.level = RAW_LOCK_LOCKX; + parm[0].lockx.in.fnum = gen_fnum(instance); + parm[0].lockx.in.mode = gen_lock_mode(); + parm[0].lockx.in.timeout = gen_timeout(); + do { + /* make sure we don't accidentially generate an oplock + break ack - otherwise the server can just block forever */ + parm[0].lockx.in.ulock_cnt = gen_lock_count(); + parm[0].lockx.in.lock_cnt = gen_lock_count(); + nlocks = parm[0].lockx.in.ulock_cnt + parm[0].lockx.in.lock_cnt; + } while (nlocks == 0); + + if (nlocks > 0) { + parm[0].lockx.in.locks = talloc(current_op.mem_ctx, + sizeof(parm[0].lockx.in.locks[0]) * nlocks); + for (n=0;ngeneric.level = levels[i].level; +} + +/* + compare returned fileinfo structures +*/ +static BOOL cmp_fileinfo(int instance, + union smb_fileinfo parm[NSERVERS], + NTSTATUS status[NSERVERS]) +{ + int i; + + switch (parm[0].generic.level) { + case RAW_FILEINFO_GENERIC: + return False; + + case RAW_FILEINFO_GETATTR: + CHECK_EQUAL(getattr.out.attrib); + CHECK_EQUAL(getattr.out.size); + CHECK_TIMES_EQUAL(getattr.out.write_time); + break; + + case RAW_FILEINFO_GETATTRE: + CHECK_TIMES_EQUAL(getattre.out.create_time); + CHECK_TIMES_EQUAL(getattre.out.access_time); + CHECK_TIMES_EQUAL(getattre.out.write_time); + CHECK_EQUAL(getattre.out.size); + CHECK_EQUAL(getattre.out.alloc_size); + CHECK_EQUAL(getattre.out.attrib); + break; + + case RAW_FILEINFO_STANDARD: + CHECK_TIMES_EQUAL(standard.out.create_time); + CHECK_TIMES_EQUAL(standard.out.access_time); + CHECK_TIMES_EQUAL(standard.out.write_time); + CHECK_EQUAL(standard.out.size); + CHECK_EQUAL(standard.out.alloc_size); + CHECK_EQUAL(standard.out.attrib); + break; + + case RAW_FILEINFO_EA_SIZE: + CHECK_TIMES_EQUAL(ea_size.out.create_time); + CHECK_TIMES_EQUAL(ea_size.out.access_time); + CHECK_TIMES_EQUAL(ea_size.out.write_time); + CHECK_EQUAL(ea_size.out.size); + CHECK_EQUAL(ea_size.out.alloc_size); + CHECK_EQUAL(ea_size.out.attrib); + CHECK_EQUAL(ea_size.out.ea_size); + break; + + case RAW_FILEINFO_ALL_EAS: + CHECK_EQUAL(all_eas.out.num_eas); + for (i=0;igeneric.level = levels[i].level; + + switch (info->generic.level) { + case RAW_SFILEINFO_SETATTR: + info->setattr.in.attrib = gen_attrib(); + info->setattr.in.write_time = gen_timet(); + break; + case RAW_SFILEINFO_SETATTRE: + info->setattre.in.create_time = gen_timet(); + info->setattre.in.access_time = gen_timet(); + info->setattre.in.write_time = gen_timet(); + break; + case RAW_SFILEINFO_STANDARD: + info->standard.in.create_time = gen_timet(); + info->standard.in.access_time = gen_timet(); + info->standard.in.write_time = gen_timet(); + break; + case RAW_SFILEINFO_EA_SET: + info->ea_set.in.ea = gen_ea_struct(); + break; + case RAW_SFILEINFO_BASIC_INFO: + case RAW_SFILEINFO_BASIC_INFORMATION: + info->basic_info.in.create_time = gen_nttime(); + info->basic_info.in.access_time = gen_nttime(); + info->basic_info.in.write_time = gen_nttime(); + info->basic_info.in.change_time = gen_nttime(); + info->basic_info.in.attrib = gen_attrib(); + break; + case RAW_SFILEINFO_DISPOSITION_INFO: + case RAW_SFILEINFO_DISPOSITION_INFORMATION: + info->disposition_info.in.delete_on_close = gen_bool(); + break; + case RAW_SFILEINFO_ALLOCATION_INFO: + case RAW_SFILEINFO_ALLOCATION_INFORMATION: + info->allocation_info.in.alloc_size = gen_alloc_size(); + break; + case RAW_SFILEINFO_END_OF_FILE_INFO: + case RAW_SFILEINFO_END_OF_FILE_INFORMATION: + info->end_of_file_info.in.size = gen_offset(); + break; + case RAW_SFILEINFO_RENAME_INFORMATION: + info->rename_information.in.overwrite = gen_bool(); + info->rename_information.in.root_fid = gen_root_fid(instance); + info->rename_information.in.new_name = gen_fname_open(instance); + break; + case RAW_SFILEINFO_POSITION_INFORMATION: + info->position_information.in.position = gen_offset(); + break; + case RAW_SFILEINFO_MODE_INFORMATION: + info->mode_information.in.mode = gen_bits_mask(0xFFFFFFFF); + break; + } +} + +/* + generate setpathinfo operations +*/ +static BOOL handler_spathinfo(int instance) +{ + union smb_setfileinfo parm[NSERVERS]; + NTSTATUS status[NSERVERS]; + + parm[0].generic.file.fname = gen_fname_open(instance); + + gen_setfileinfo(instance, &parm[0]); + + GEN_COPY_PARM; + + /* a special case for the fid in a RENAME */ + if (parm[0].generic.level == RAW_SFILEINFO_RENAME_INFORMATION && + parm[0].rename_information.in.root_fid != 0) { + GEN_SET_FNUM(rename_information.in.root_fid); + } + + GEN_CALL(smb_raw_setpathinfo(tree, &parm[i])); + + return True; +} + + +/* + generate setfileinfo operations +*/ +static BOOL handler_sfileinfo(int instance) +{ + union smb_setfileinfo parm[NSERVERS]; + NTSTATUS status[NSERVERS]; + + parm[0].generic.file.fnum = gen_fnum(instance); + + gen_setfileinfo(instance, &parm[0]); + + GEN_COPY_PARM; + GEN_SET_FNUM(generic.file.fnum); + GEN_CALL(smb_raw_setfileinfo(tree, &parm[i])); + + return True; +} + + +/* + generate change notify operations +*/ +static BOOL handler_notify(int instance) +{ + struct smb_notify parm[NSERVERS]; + int n; + + parm[0].in.buffer_size = gen_io_count(); + parm[0].in.completion_filter = gen_bits_mask(0xFF); + parm[0].in.fnum = gen_fnum(instance); + parm[0].in.recursive = gen_bool(); + + GEN_COPY_PARM; + GEN_SET_FNUM(in.fnum); + + for (n=0;ntree, &parm[n]); + req->async.fn = async_notify; + } + + return True; +} + +/* + wipe any relevant files +*/ +static void wipe_files(void) +{ + int i; + for (i=0;i 0) { + printf("Deleted %d files on server %d\n", n, i); + } + } +} + +/* + dump the current seeds - useful for continuing a backtrack +*/ +static void dump_seeds(void) +{ + int i; + FILE *f; + + if (!options.seeds_file) { + return; + } + f = fopen("seeds.tmp", "w"); + if (!f) return; + + for (i=0;i 0 && base+chunk < options.numops && options.numops > 1; ) { + int i, max; + + chunk = MIN(chunk, options.numops / 2); + + /* mark this range as disabled */ + max = MIN(options.numops, base+chunk); + for (i=base;i 0); + + printf("Reduced to %d ops\n", options.numops); + ret = run_test(); + if (ret != options.numops - 1) { + printf("Inconsistent result? ret=%d numops=%d\n", ret, options.numops); + } +} + +/* + start the main gentest process +*/ +static BOOL start_gentest(void) +{ + int op; + int ret; + + /* allocate the open_handles array */ + open_handles = calloc(options.max_open_handles, sizeof(open_handles[0])); + + srandom(options.seed); + op_parms = calloc(options.numops, sizeof(op_parms[0])); + + /* generate the seeds - after this everything is deterministic */ + if (options.use_preset_seeds) { + int numops; + char **preset = file_lines_load(options.seeds_file, &numops); + if (!preset) { + printf("Failed to load %s - %s\n", options.seeds_file, strerror(errno)); + exit(1); + } + if (numops < options.numops) { + options.numops = numops; + } + for (op=0;opconn; + 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 %s:%s\n", + conn, f, + (double)start, (double)len, + op==READ_LOCK?"READ_LOCK":"WRITE_LOCK", + nt_errstr(status[0]), nt_errstr(status[1])); + } + if (!NT_STATUS_EQUAL(status[0],status[1])) return False; + break; + + case OP_UNLOCK: + /* unset a lock */ + for (server=0;server %s:%s\n", + conn, f, + (double)start, (double)len, + nt_errstr(status[0]), nt_errstr(status[1])); + } + if (!hide_unlock_fails && !NT_STATUS_EQUAL(status[0],status[1])) + return False; + break; + + case OP_REOPEN: + /* reopen the file */ + for (server=0;server 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;iuse_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;serverconn; + 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 %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 %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 %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_by_string(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': + 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; +} + +static 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->transport->negotiate.protocol)==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 +*******************************************************/ +static struct cli_state *connect_one(char *share) +{ + struct cli_state *c; + fstring server; + uint_t flags = 0; + NTSTATUS status; + + fstrcpy(server,share+2); + share = strchr_m(server,'\\'); + if (!share) return NULL; + *share = 0; + share++; + + status = cli_full_connection(&c, "masktest", + server, NULL, + share, "?????", + username, lp_workgroup(), + password, flags, NULL); + + if (!NT_STATUS_IS_OK(status)) { + return NULL; + } + + return c; +} + +static char *resultp; +static struct { + pstring long_name; + pstring short_name; +} last_hit; +static BOOL f_info_hit; + +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] = '+'; + } + pstrcpy(last_hit.long_name, f->name); + pstrcpy(last_hit.short_name, f->short_name); + f_info_hit = True; +} + +static void get_real_name(struct cli_state *cli, + pstring long_name, fstring short_name) +{ + const char *mask; + if (max_protocol <= PROTOCOL_LANMAN1) { + mask = "\\masktest\\*.*"; + } else { + mask = "\\masktest\\*"; + } + + f_info_hit = False; + + cli_list_new(cli, mask, FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_DIRECTORY, + listfn, NULL); + + if (f_info_hit) { + fstrcpy(short_name, last_hit.short_name); + strlower(short_name); + pstrcpy(long_name, last_hit.long_name); + strlower(long_name); + } + + if (*short_name == 0) { + fstrcpy(short_name, long_name); + } +} + +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, ""); + get_real_name(cli, long_name, short_name); + fstrcpy(res1, "---"); + cli_list(cli, mask, FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_DIRECTORY, + listfn, NULL); + + res2 = reg_test(cli, mask, long_name, short_name); + + if (showall || strcmp(res1, res2)) { + d_printf("%s %s %d mask=[%s] file=[%s] rfile=[%s/%s]\n", + res1, res2, count, mask, file, long_name, short_name); + if (die_on_error) exit(1); + } + + 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 0) 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/source4/torture/nsstest.c b/source4/torture/nsstest.c new file mode 100644 index 0000000000..a82fa05203 --- /dev/null +++ b/source4/torture/nsstest.c @@ -0,0 +1,410 @@ +/* + Unix SMB/CIFS implementation. + nss tester for winbindd + 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 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) +{ + char s[1024]; + static void *h; + void *res; + + snprintf(s,sizeof(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); + 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; + + 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; + + 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; + + 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; + 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; + 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 (!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 (!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 (!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; + 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; + 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:%d:%d:%s:%s:%s\n", + pwd->pw_name, + pwd->pw_passwd, + pwd->pw_uid, + pwd->pw_gid, + pwd->pw_gecos, + pwd->pw_dir, + pwd->pw_shell); +} + +static void print_group(struct group *grp) +{ + int i; + printf("%s:%s:%d: ", + grp->gr_name, + grp->gr_passwd, + 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; ipw_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/source4/torture/qfileinfo.c b/source4/torture/qfileinfo.c new file mode 100644 index 0000000000..d7136cf22a --- /dev/null +++ b/source4/torture/qfileinfo.c @@ -0,0 +1,640 @@ +/* + Unix SMB/CIFS implementation. + RAW_FILEINFO_* individual test suite + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 { + const char *name; + enum fileinfo_level level; + unsigned only_paths:1; + unsigned only_handles:1; + NTSTATUS fnum_status, fname_status; + union smb_fileinfo fnum_finfo, fname_finfo; +} levels[] = { + { "GETATTR", RAW_FILEINFO_GETATTR, 1, 0, }, + { "GETATTRE", RAW_FILEINFO_GETATTRE, 0, 1, }, + { "STANDARD", RAW_FILEINFO_STANDARD, }, + { "EA_SIZE", RAW_FILEINFO_EA_SIZE, }, + { "ALL_EAS", RAW_FILEINFO_ALL_EAS, }, + { "IS_NAME_VALID", RAW_FILEINFO_IS_NAME_VALID, 1, 0, }, + { "BASIC_INFO", RAW_FILEINFO_BASIC_INFO, }, + { "STANDARD_INFO", RAW_FILEINFO_STANDARD_INFO, }, + { "EA_INFO", RAW_FILEINFO_EA_INFO, }, + { "NAME_INFO", RAW_FILEINFO_NAME_INFO, }, + { "ALL_INFO", RAW_FILEINFO_ALL_INFO, }, + { "ALT_NAME_INFO", RAW_FILEINFO_ALT_NAME_INFO, }, + { "STREAM_INFO", RAW_FILEINFO_STREAM_INFO, }, + { "COMPRESSION_INFO", RAW_FILEINFO_COMPRESSION_INFO, }, + { "BASIC_INFORMATION", RAW_FILEINFO_BASIC_INFORMATION, }, + { "STANDARD_INFORMATION", RAW_FILEINFO_STANDARD_INFORMATION, }, + { "INTERNAL_INFORMATION", RAW_FILEINFO_INTERNAL_INFORMATION, }, + { "EA_INFORMATION", RAW_FILEINFO_EA_INFORMATION, }, + { "ACCESS_INFORMATION", RAW_FILEINFO_ACCESS_INFORMATION, }, + { "NAME_INFORMATION", RAW_FILEINFO_NAME_INFORMATION, }, + { "POSITION_INFORMATION", RAW_FILEINFO_POSITION_INFORMATION, }, + { "MODE_INFORMATION", RAW_FILEINFO_MODE_INFORMATION, }, + { "ALIGNMENT_INFORMATION", RAW_FILEINFO_ALIGNMENT_INFORMATION, }, + { "ALL_INFORMATION", RAW_FILEINFO_ALL_INFORMATION, }, + { "ALT_NAME_INFORMATION", RAW_FILEINFO_ALT_NAME_INFORMATION, }, + { "STREAM_INFORMATION", RAW_FILEINFO_STREAM_INFORMATION, }, + { "COMPRESSION_INFORMATION", RAW_FILEINFO_COMPRESSION_INFORMATION, }, + { "NETWORK_OPEN_INFORMATION", RAW_FILEINFO_NETWORK_OPEN_INFORMATION, }, + { "ATTRIBUTE_TAG_INFORMATION", RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION, }, + { NULL, } +}; + +/* + compare a dos time (2 second resolution) to a nt time +*/ +static int dos_nt_time_cmp(time_t t, const NTTIME *nt) +{ + time_t t2 = nt_time_to_unix(nt); + if (ABS(t2 - t) <= 2) return 0; + return t2 - t; +} + + +/* + find a level in the levels[] table +*/ +static union smb_fileinfo *fnum_find(const char *name) +{ + int i; + for (i=0; levels[i].name; i++) { + if (NT_STATUS_IS_OK(levels[i].fnum_status) && + strcmp(name, levels[i].name) == 0 && + !levels[i].only_paths) { + return &levels[i].fnum_finfo; + } + } + return NULL; +} + +/* + find a level in the levels[] table +*/ +static union smb_fileinfo *fname_find(const char *name) +{ + int i; + for (i=0; levels[i].name; i++) { + if (NT_STATUS_IS_OK(levels[i].fname_status) && + strcmp(name, levels[i].name) == 0 && + !levels[i].only_handles) { + return &levels[i].fname_finfo; + } + } + return NULL; +} + +/* local macros to make the code below more readable */ +#define VAL_EQUAL(n1, v1, n2, v2) do {if (s1->n1.out.v1 != s2->n2.out.v2) { \ + printf("%s/%s [%u] != %s/%s [%u] at %s(%d)\n", \ + #n1, #v1, (uint_t)s1->n1.out.v1, \ + #n2, #v2, (uint_t)s2->n2.out.v2, \ + __FILE__, __LINE__); \ + ret = False; \ +}} while(0) + +#define STR_EQUAL(n1, v1, n2, v2) do {if (strcmp(s1->n1.out.v1.s, s2->n2.out.v2.s) || \ + s1->n1.out.v1.private_length != s2->n2.out.v2.private_length) { \ + printf("%s/%s [%s/%d] != %s/%s [%s/%d] at %s(%d)\n", \ + #n1, #v1, s1->n1.out.v1.s, s1->n1.out.v1.private_length, \ + #n2, #v2, s2->n2.out.v2.s, s2->n2.out.v2.private_length, \ + __FILE__, __LINE__); \ + ret = False; \ +}} while(0) + +#define STRUCT_EQUAL(n1, v1, n2, v2) do {if (memcmp(&s1->n1.out.v1,&s2->n2.out.v2,sizeof(s1->n1.out.v1))) { \ + printf("%s/%s != %s/%s at %s(%d)\n", \ + #n1, #v1, \ + #n2, #v2, \ + __FILE__, __LINE__); \ + ret = False; \ +}} while(0) + +/* used to find hints on unknown values - and to make sure + we zero-fill */ +#define VAL_UNKNOWN(n1, v1) do {if (s1->n1.out.v1 != 0) { \ + printf("%s/%s non-zero unknown - %u (0x%x) at %s(%d)\n", \ + #n1, #v1, \ + (uint_t)s1->n1.out.v1, \ + (uint_t)s1->n1.out.v1, \ + __FILE__, __LINE__); \ + ret = False; \ +}} while(0) + +/* basic testing of all RAW_FILEINFO_* calls + for each call we test that it succeeds, and where possible test + for consistency between the calls. +*/ +BOOL torture_qfileinfo(int dummy) +{ + struct cli_state *cli; + int i; + BOOL ret = True; + int count; + union smb_fileinfo *s1, *s2; + TALLOC_CTX *mem_ctx; + int fnum; + const char *fname = "\\torture_qfileinfo.txt"; + NTTIME correct_time; + large_t correct_size; + uint32 correct_attrib; + const char *correct_name; + BOOL skip_streams = False; + + if (!torture_open_connection(&cli)) { + return False; + } + + mem_ctx = talloc_init("torture_qfileinfo"); + + fnum = create_complex_file(cli, mem_ctx, fname); + if (fnum == -1) { + printf("ERROR: open of %s failed (%s)\n", fname, cli_errstr(cli)); + ret = False; + goto done; + } + + + /* scan all the fileinfo and pathinfo levels */ + for (i=0; levels[i].name; i++) { + if (!levels[i].only_paths) { + levels[i].fnum_finfo.generic.level = levels[i].level; + levels[i].fnum_finfo.generic.in.fnum = fnum; + levels[i].fnum_status = smb_raw_fileinfo(cli->tree, mem_ctx, + &levels[i].fnum_finfo); + } + + if (!levels[i].only_handles) { + levels[i].fname_finfo.generic.level = levels[i].level; + levels[i].fname_finfo.generic.in.fname = talloc_strdup(mem_ctx, fname); + levels[i].fname_status = smb_raw_pathinfo(cli->tree, mem_ctx, + &levels[i].fname_finfo); + } + } + + /* check for completely broken levels */ + for (count=i=0; levels[i].name; i++) { + if (!levels[i].only_paths && !NT_STATUS_IS_OK(levels[i].fnum_status)) { + printf("ERROR: level %s failed - %s\n", + levels[i].name, nt_errstr(levels[i].fnum_status)); + count++; + } + if (!levels[i].only_handles && !NT_STATUS_IS_OK(levels[i].fname_status)) { + printf("ERROR: level %s failed - %s\n", + levels[i].name, nt_errstr(levels[i].fname_status)); + count++; + } + } + + if (count != 0) { + ret = False; + printf("%d levels failed\n", count); + if (count > 32) { + printf("too many level failures - giving up\n"); + goto done; + } + } + + /* see if we can do streams */ + s1 = fnum_find("STREAM_INFO"); + if (!s1 || s1->stream_info.out.num_streams == 0) { + printf("STREAM_INFO broken (%d) - skipping streams checks\n", + s1 ? s1->stream_info.out.num_streams : -1); + skip_streams = True; + } + + + /* this code is incredibly repititive but doesn't lend itself to loops, so + we use lots of macros to make it less painful */ + + /* first off we check the levels that are supposed to be aliases. It will be quite rare for + this code to fail, but we need to check it for completeness */ + + + +#define ALIAS_CHECK(sname1, sname2) \ + do { \ + s1 = fnum_find(sname1); s2 = fnum_find(sname2); \ + if (s1 && s2) { INFO_CHECK } \ + s1 = fname_find(sname1); s2 = fname_find(sname2); \ + if (s1 && s2) { INFO_CHECK } \ + s1 = fnum_find(sname1); s2 = fname_find(sname2); \ + if (s1 && s2) { INFO_CHECK } \ + } while (0) + +#define INFO_CHECK \ + STRUCT_EQUAL(basic_info, create_time, basic_info, create_time); \ + STRUCT_EQUAL(basic_info, access_time, basic_info, access_time); \ + STRUCT_EQUAL(basic_info, write_time, basic_info, write_time); \ + STRUCT_EQUAL(basic_info, change_time, basic_info, change_time); \ + VAL_EQUAL (basic_info, attrib, basic_info, attrib); + + ALIAS_CHECK("BASIC_INFO", "BASIC_INFORMATION"); + +#undef INFO_CHECK +#define INFO_CHECK \ + VAL_EQUAL(standard_info, alloc_size, standard_info, alloc_size); \ + VAL_EQUAL(standard_info, size, standard_info, size); \ + VAL_EQUAL(standard_info, nlink, standard_info, nlink); \ + VAL_EQUAL(standard_info, delete_pending, standard_info, delete_pending); \ + VAL_EQUAL(standard_info, directory, standard_info, directory); + + ALIAS_CHECK("STANDARD_INFO", "STANDARD_INFORMATION"); + +#undef INFO_CHECK +#define INFO_CHECK \ + VAL_EQUAL(ea_info, ea_size, ea_info, ea_size); + + ALIAS_CHECK("EA_INFO", "EA_INFORMATION"); + +#undef INFO_CHECK +#define INFO_CHECK \ + STR_EQUAL(name_info, fname, name_info, fname); + + ALIAS_CHECK("NAME_INFO", "NAME_INFORMATION"); + +#undef INFO_CHECK +#define INFO_CHECK \ + STRUCT_EQUAL(all_info, create_time, all_info, create_time); \ + STRUCT_EQUAL(all_info, access_time, all_info, access_time); \ + STRUCT_EQUAL(all_info, write_time, all_info, write_time); \ + STRUCT_EQUAL(all_info, change_time, all_info, change_time); \ + VAL_EQUAL(all_info, attrib, all_info, attrib); \ + VAL_EQUAL(all_info, alloc_size, all_info, alloc_size); \ + VAL_EQUAL(all_info, size, all_info, size); \ + VAL_EQUAL(all_info, nlink, all_info, nlink); \ + VAL_EQUAL(all_info, delete_pending, all_info, delete_pending); \ + VAL_EQUAL(all_info, directory, all_info, directory); \ + VAL_EQUAL(all_info, ea_size, all_info, ea_size); \ + STR_EQUAL(all_info, fname, all_info, fname); + + ALIAS_CHECK("ALL_INFO", "ALL_INFORMATION"); + + +#undef INFO_CHECK +#define INFO_CHECK \ + STR_EQUAL(alt_name_info, fname, alt_name_info, fname); + + ALIAS_CHECK("ALT_NAME_INFO", "ALT_NAME_INFORMATION"); + +#define TIME_CHECK_NT(sname, stype, tfield) do { \ + s1 = fnum_find(sname); \ + if (s1 && memcmp(&s1->stype.out.tfield, &correct_time, sizeof(correct_time)) != 0) { \ + printf("(%d) handle %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \ + nt_time_string(mem_ctx, &s1->stype.out.tfield), \ + nt_time_string(mem_ctx, &correct_time)); \ + ret = False; \ + } \ + s1 = fname_find(sname); \ + if (s1 && memcmp(&s1->stype.out.tfield, &correct_time, sizeof(correct_time)) != 0) { \ + printf("(%d) path %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \ + nt_time_string(mem_ctx, &s1->stype.out.tfield), \ + nt_time_string(mem_ctx, &correct_time)); \ + ret = False; \ + }} while (0) + +#define TIME_CHECK_DOS(sname, stype, tfield) do { \ + s1 = fnum_find(sname); \ + if (s1 && dos_nt_time_cmp(s1->stype.out.tfield, &correct_time) != 0) { \ + printf("(%d) handle %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \ + time_string(mem_ctx, s1->stype.out.tfield), \ + nt_time_string(mem_ctx, &correct_time)); \ + ret = False; \ + } \ + s1 = fname_find(sname); \ + if (s1 && dos_nt_time_cmp(s1->stype.out.tfield, &correct_time) != 0) { \ + printf("(%d) path %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \ + time_string(mem_ctx, s1->stype.out.tfield), \ + nt_time_string(mem_ctx, &correct_time)); \ + ret = False; \ + }} while (0) + +#define TIME_CHECK_UNX(sname, stype, tfield) do { \ + s1 = fnum_find(sname); \ + if (s1 && unx_nt_time_cmp(s1->stype.out.tfield, &correct_time) != 0) { \ + printf("(%d) handle %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \ + time_string(mem_ctx, s1->stype.out.tfield), \ + nt_time_string(mem_ctx, &correct_time)); \ + ret = False; \ + } \ + s1 = fname_find(sname); \ + if (s1 && unx_nt_time_cmp(s1->stype.out.tfield, &correct_time) != 0) { \ + printf("(%d) path %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \ + time_string(mem_ctx, s1->stype.out.tfield), \ + nt_time_string(mem_ctx, &correct_time)); \ + ret = False; \ + }} while (0) + + /* now check that all the times that are supposed to be equal are correct */ + s1 = fnum_find("BASIC_INFO"); + correct_time = s1->basic_info.out.create_time; + printf("create_time: %s\n", nt_time_string(mem_ctx, &correct_time)); + + TIME_CHECK_NT ("BASIC_INFO", basic_info, create_time); + TIME_CHECK_NT ("BASIC_INFORMATION", basic_info, create_time); + TIME_CHECK_DOS("GETATTRE", getattre, create_time); + TIME_CHECK_DOS("STANDARD", standard, create_time); + TIME_CHECK_DOS("EA_SIZE", ea_size, create_time); + TIME_CHECK_NT ("ALL_INFO", all_info, create_time); + TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information, create_time); + + s1 = fnum_find("BASIC_INFO"); + correct_time = s1->basic_info.out.access_time; + printf("access_time: %s\n", nt_time_string(mem_ctx, &correct_time)); + + TIME_CHECK_NT ("BASIC_INFO", basic_info, access_time); + TIME_CHECK_NT ("BASIC_INFORMATION", basic_info, access_time); + TIME_CHECK_DOS("GETATTRE", getattre, access_time); + TIME_CHECK_DOS("STANDARD", standard, access_time); + TIME_CHECK_DOS("EA_SIZE", ea_size, access_time); + TIME_CHECK_NT ("ALL_INFO", all_info, access_time); + TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information, access_time); + + s1 = fnum_find("BASIC_INFO"); + correct_time = s1->basic_info.out.write_time; + printf("write_time : %s\n", nt_time_string(mem_ctx, &correct_time)); + + TIME_CHECK_NT ("BASIC_INFO", basic_info, write_time); + TIME_CHECK_NT ("BASIC_INFORMATION", basic_info, write_time); + TIME_CHECK_DOS("GETATTRE", getattre, write_time); + TIME_CHECK_DOS("STANDARD", standard, write_time); + TIME_CHECK_DOS("EA_SIZE", ea_size, write_time); + TIME_CHECK_NT ("ALL_INFO", all_info, write_time); + TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information, write_time); + + s1 = fnum_find("BASIC_INFO"); + correct_time = s1->basic_info.out.change_time; + printf("change_time: %s\n", nt_time_string(mem_ctx, &correct_time)); + + TIME_CHECK_NT ("BASIC_INFO", basic_info, change_time); + TIME_CHECK_NT ("BASIC_INFORMATION", basic_info, change_time); + TIME_CHECK_NT ("ALL_INFO", all_info, change_time); + TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information, change_time); + + +#define SIZE_CHECK(sname, stype, tfield) do { \ + s1 = fnum_find(sname); \ + if (s1 && s1->stype.out.tfield != correct_size) { \ + printf("(%d) handle %s/%s incorrect - %u should be %u\n", __LINE__, #stype, #tfield, \ + (unsigned)s1->stype.out.tfield, \ + (unsigned)correct_size); \ + ret = False; \ + } \ + s1 = fname_find(sname); \ + if (s1 && s1->stype.out.tfield != correct_size) { \ + printf("(%d) path %s/%s incorrect - %u should be %u\n", __LINE__, #stype, #tfield, \ + (unsigned)s1->stype.out.tfield, \ + (unsigned)correct_size); \ + ret = False; \ + }} while (0) + + s1 = fnum_find("STANDARD_INFO"); + correct_size = s1->standard_info.out.size; + printf("size: %u\n", (unsigned)correct_size); + + SIZE_CHECK("GETATTR", getattr, size); + SIZE_CHECK("GETATTRE", getattre, size); + SIZE_CHECK("STANDARD", standard, size); + SIZE_CHECK("EA_SIZE", ea_size, size); + SIZE_CHECK("STANDARD_INFO", standard_info, size); + SIZE_CHECK("STANDARD_INFORMATION", standard_info, size); + SIZE_CHECK("ALL_INFO", all_info, size); + SIZE_CHECK("ALL_INFORMATION", all_info, size); + SIZE_CHECK("COMPRESSION_INFO", compression_info, compressed_size); + SIZE_CHECK("COMPRESSION_INFORMATION", compression_info, compressed_size); + SIZE_CHECK("NETWORK_OPEN_INFORMATION", network_open_information, size); + if (!skip_streams) { + SIZE_CHECK("STREAM_INFO", stream_info, streams[0].size); + SIZE_CHECK("STREAM_INFORMATION", stream_info, streams[0].size); + } + + + s1 = fnum_find("STANDARD_INFO"); + correct_size = s1->standard_info.out.alloc_size; + printf("alloc_size: %u\n", (unsigned)correct_size); + + SIZE_CHECK("GETATTRE", getattre, alloc_size); + SIZE_CHECK("STANDARD", standard, alloc_size); + SIZE_CHECK("EA_SIZE", ea_size, alloc_size); + SIZE_CHECK("STANDARD_INFO", standard_info, alloc_size); + SIZE_CHECK("STANDARD_INFORMATION", standard_info, alloc_size); + SIZE_CHECK("ALL_INFO", all_info, alloc_size); + SIZE_CHECK("ALL_INFORMATION", all_info, alloc_size); + SIZE_CHECK("NETWORK_OPEN_INFORMATION", network_open_information, alloc_size); + if (!skip_streams) { + SIZE_CHECK("STREAM_INFO", stream_info, streams[0].alloc_size); + SIZE_CHECK("STREAM_INFORMATION", stream_info, streams[0].alloc_size); + } + +#define ATTRIB_CHECK(sname, stype, tfield) do { \ + s1 = fnum_find(sname); \ + if (s1 && s1->stype.out.tfield != correct_attrib) { \ + printf("(%d) handle %s/%s incorrect - 0x%x should be 0x%x\n", __LINE__, #stype, #tfield, \ + (unsigned)s1->stype.out.tfield, \ + (unsigned)correct_attrib); \ + ret = False; \ + } \ + s1 = fname_find(sname); \ + if (s1 && s1->stype.out.tfield != correct_attrib) { \ + printf("(%d) path %s/%s incorrect - 0x%x should be 0x%x\n", __LINE__, #stype, #tfield, \ + (unsigned)s1->stype.out.tfield, \ + (unsigned)correct_attrib); \ + ret = False; \ + }} while (0) + + s1 = fnum_find("BASIC_INFO"); + correct_attrib = s1->basic_info.out.attrib; + printf("attrib: 0x%x\n", (unsigned)correct_attrib); + + ATTRIB_CHECK("GETATTR", getattr, attrib); + ATTRIB_CHECK("GETATTRE", getattre, attrib); + ATTRIB_CHECK("STANDARD", standard, attrib); + ATTRIB_CHECK("BASIC_INFO", basic_info, attrib); + ATTRIB_CHECK("BASIC_INFORMATION", basic_info, attrib); + ATTRIB_CHECK("EA_SIZE", ea_size, attrib); + ATTRIB_CHECK("ALL_INFO", all_info, attrib); + ATTRIB_CHECK("ALL_INFORMATION", all_info, attrib); + ATTRIB_CHECK("NETWORK_OPEN_INFORMATION", network_open_information, attrib); + ATTRIB_CHECK("ATTRIBUTE_TAG_INFORMATION", attribute_tag_information, attrib); + + correct_name = fname; + printf("name: %s\n", correct_name); + +#define NAME_CHECK(sname, stype, tfield, flags) do { \ + s1 = fnum_find(sname); \ + if ((s1 && strcmp(s1->stype.out.tfield.s, correct_name) != 0) || \ + wire_bad_flags(&s1->stype.out.tfield, flags)) { \ + printf("(%d) handle %s/%s incorrect - '%s/%d'\n", __LINE__, #stype, #tfield, \ + s1->stype.out.tfield.s, s1->stype.out.tfield.private_length); \ + ret = False; \ + } \ + s1 = fname_find(sname); \ + if ((s1 && strcmp(s1->stype.out.tfield.s, correct_name)) != 0 || \ + wire_bad_flags(&s1->stype.out.tfield, flags)) { \ + printf("(%d) path %s/%s incorrect - '%s/%d'\n", __LINE__, #stype, #tfield, \ + s1->stype.out.tfield.s, s1->stype.out.tfield.private_length); \ + ret = False; \ + }} while (0) + + NAME_CHECK("NAME_INFO", name_info, fname, STR_UNICODE); + NAME_CHECK("NAME_INFORMATION", name_info, fname, STR_UNICODE); + + /* the ALL_INFO file name is the full path on the filesystem */ + s1 = fnum_find("ALL_INFO"); + if (s1 && !s1->all_info.out.fname.s) { + printf("ALL_INFO didn't give a filename\n"); + ret = False; + } + if (s1 && s1->all_info.out.fname.s) { + char *p = strrchr(s1->all_info.out.fname.s, '\\'); + if (!p) { + printf("Not a full path in all_info/fname? - '%s'\n", + s1->all_info.out.fname.s); + ret = False; + } else { + if (strcmp(correct_name, p) != 0) { + printf("incorrect basename in all_info/fname - '%s'\n", + s1->all_info.out.fname.s); + ret = False; + } + } + if (wire_bad_flags(&s1->all_info.out.fname, STR_UNICODE)) { + printf("Should not null terminate all_info/fname\n"); + ret = False; + } + } + + s1 = fnum_find("ALT_NAME_INFO"); + correct_name = s1->alt_name_info.out.fname.s; + printf("alt_name: %s\n", correct_name); + + NAME_CHECK("ALT_NAME_INFO", alt_name_info, fname, STR_UNICODE); + NAME_CHECK("ALT_NAME_INFORMATION", alt_name_info, fname, STR_UNICODE); + + /* and make sure we can open by alternate name */ + cli_close(cli, fnum); + fnum = cli_nt_create_full(cli, correct_name, 0, NT_ACCESS_GENERIC_ALL_ACCESS, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_DELETE| + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE, + FILE_OVERWRITE_IF, + 0, 0); + if (fnum == -1) { + printf("Unable to open by alt_name - %s\n", cli_errstr(cli)); + ret = False; + } + + if (!skip_streams) { + correct_name = "::$DATA"; + printf("stream_name: %s\n", correct_name); + + NAME_CHECK("STREAM_INFO", stream_info, streams[0].stream_name, STR_UNICODE); + NAME_CHECK("STREAM_INFORMATION", stream_info, streams[0].stream_name, STR_UNICODE); + } + + /* make sure the EAs look right */ + s1 = fnum_find("ALL_EAS"); + if (s1) { + printf("ea_size: %d\n", s1->all_eas.out.ea_size); + for (i=0;iall_eas.out.num_eas;i++) { + printf(" flags=%d %s=%*.*s\n", + s1->all_eas.out.eas[i].flags, + s1->all_eas.out.eas[i].name.s, + s1->all_eas.out.eas[i].value.length, + s1->all_eas.out.eas[i].value.length, + s1->all_eas.out.eas[i].value.data); + } + } + + +#define VAL_CHECK(sname1, stype1, tfield1, sname2, stype2, tfield2) do { \ + s1 = fnum_find(sname1); s2 = fnum_find(sname2); \ + if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \ + printf("(%d) handle %s/%s != %s/%s - 0x%x vs 0x%x\n", __LINE__, \ + #stype1, #tfield1, #stype2, #tfield2, \ + s1->stype1.out.tfield1, s2->stype2.out.tfield2); \ + ret = False; \ + } \ + s1 = fname_find(sname1); s2 = fname_find(sname2); \ + if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \ + printf("(%d) path %s/%s != %s/%s - 0x%x vs 0x%x\n", __LINE__, \ + #stype1, #tfield1, #stype2, #tfield2, \ + s1->stype1.out.tfield1, s2->stype2.out.tfield2); \ + ret = False; \ + } \ + s1 = fnum_find(sname1); s2 = fname_find(sname2); \ + if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \ + printf("(%d) handle %s/%s != path %s/%s - 0x%x vs 0x%x\n", __LINE__, \ + #stype1, #tfield1, #stype2, #tfield2, \ + s1->stype1.out.tfield1, s2->stype2.out.tfield2); \ + ret = False; \ + } \ + s1 = fname_find(sname1); s2 = fnum_find(sname2); \ + if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \ + printf("(%d) path %s/%s != handle %s/%s - 0x%x vs 0x%x\n", __LINE__, \ + #stype1, #tfield1, #stype2, #tfield2, \ + s1->stype1.out.tfield1, s2->stype2.out.tfield2); \ + ret = False; \ + }} while (0) + + VAL_CHECK("STANDARD_INFO", standard_info, delete_pending, + "ALL_INFO", all_info, delete_pending); + VAL_CHECK("STANDARD_INFO", standard_info, directory, + "ALL_INFO", all_info, directory); + VAL_CHECK("STANDARD_INFO", standard_info, nlink, + "ALL_INFO", all_info, nlink); + VAL_CHECK("EA_INFO", ea_info, ea_size, + "ALL_INFO", all_info, ea_size); + VAL_CHECK("ALL_EAS", all_eas, ea_size, + "ALL_INFO", all_info, ea_size); + VAL_CHECK("EA_SIZE", ea_size, ea_size, + "ALL_INFO", all_info, ea_size); + +#define UNKNOWN_CHECK(sname, stype, tfield) do { \ + s1 = fnum_find(sname); \ + if (s1 && s1->stype.out.tfield != 0) { \ + printf("(%d) handle %s/%s unknown != 0 (0x%x)\n", __LINE__, \ + #stype, #tfield, \ + (unsigned)s1->stype.out.tfield); \ + } \ + s1 = fname_find(sname); \ + if (s1 && s1->stype.out.tfield != 0) { \ + printf("(%d) path %s/%s unknown != 0 (0x%x)\n", __LINE__, \ + #stype, #tfield, \ + (unsigned)s1->stype.out.tfield); \ + }} while (0) + + /* now get a bit fancier .... */ + + /* when we set the delete disposition then the link count should drop + to 0 and delete_pending should be 1 */ + + +done: + cli_close(cli, fnum); + cli_unlink(cli, fname); + + torture_close_connection(cli); + talloc_destroy(mem_ctx); + return ret; +} diff --git a/source4/torture/qfsinfo.c b/source4/torture/qfsinfo.c new file mode 100644 index 0000000000..fc4947e158 --- /dev/null +++ b/source4/torture/qfsinfo.c @@ -0,0 +1,286 @@ +/* + Unix SMB/CIFS implementation. + RAW_QFS_* individual test suite + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 { + const char *name; + enum fsinfo_level level; + NTSTATUS status; + union smb_fsinfo fsinfo; +} levels[] = { + {"DSKATTR", RAW_QFS_DSKATTR, }, + {"ALLOCATION", RAW_QFS_ALLOCATION, }, + {"VOLUME", RAW_QFS_VOLUME, }, + {"VOLUME_INFO", RAW_QFS_VOLUME_INFO, }, + {"SIZE_INFO", RAW_QFS_SIZE_INFO, }, + {"DEVICE_INFO", RAW_QFS_DEVICE_INFO, }, + {"ATTRIBUTE_INFO", RAW_QFS_ATTRIBUTE_INFO, }, + {"VOLUME_INFORMATION", RAW_QFS_VOLUME_INFORMATION, }, + {"SIZE_INFORMATION", RAW_QFS_SIZE_INFORMATION, }, + {"DEVICE_INFORMATION", RAW_QFS_DEVICE_INFORMATION, }, + {"ATTRIBUTE_INFORMATION", RAW_QFS_ATTRIBUTE_INFORMATION, }, + {"QUOTA_INFORMATION", RAW_QFS_QUOTA_INFORMATION, }, + {"FULL_SIZE_INFORMATION", RAW_QFS_FULL_SIZE_INFORMATION, }, + {"OBJECTID_INFORMATION", RAW_QFS_OBJECTID_INFORMATION, }, + { NULL, } +}; + + +/* + find a level in the levels[] table +*/ +static union smb_fsinfo *find(const char *name) +{ + int i; + for (i=0; levels[i].name; i++) { + if (strcmp(name, levels[i].name) == 0) { + return &levels[i].fsinfo; + } + } + return NULL; +} + +/* local macros to make the code below more readable */ +#define VAL_EQUAL(n1, v1, n2, v2) do {if (s1->n1.out.v1 != s2->n2.out.v2) { \ + printf("%s/%s [%u] != %s/%s [%u] at %s(%d)\n", \ + #n1, #v1, (uint_t)s1->n1.out.v1, \ + #n2, #v2, (uint_t)s2->n2.out.v2, \ + __FILE__, __LINE__); \ + ret = False; \ +}} while(0) + +#define STR_EQUAL(n1, v1, n2, v2) do {if (!s1->n1.out.v1 && !s2->n2.out.v2) return True; \ + if (!s1->n1.out.v1 || !s2->n2.out.v2) return False; \ + if (strcmp(s1->n1.out.v1, s2->n2.out.v2)) { \ + printf("%s/%s [%s] != %s/%s [%s] at %s(%d)\n", \ + #n1, #v1, s1->n1.out.v1, \ + #n2, #v2, s2->n2.out.v2, \ + __FILE__, __LINE__); \ + ret = False; \ +}} while(0) + +#define STRUCT_EQUAL(n1, v1, n2, v2) do {if (memcmp(&s1->n1.out.v1,&s2->n2.out.v2,sizeof(s1->n1.out.v1))) { \ + printf("%s/%s != %s/%s at %s(%d)\n", \ + #n1, #v1, \ + #n2, #v2, \ + __FILE__, __LINE__); \ + ret = False; \ +}} while(0) + +/* used to find hints on unknown values - and to make sure + we zero-fill */ +#define VAL_UNKNOWN(n1, v1) do {if (s1->n1.out.v1 != 0) { \ + printf("%s/%s non-zero unknown - %u (0x%x) at %s(%d)\n", \ + #n1, #v1, \ + (uint_t)s1->n1.out.v1, \ + (uint_t)s1->n1.out.v1, \ + __FILE__, __LINE__); \ + ret = False; \ +}} while(0) + +/* basic testing of all RAW_QFS_* calls + for each call we test that it succeeds, and where possible test + for consistency between the calls. + + Some of the consistency tests assume that the target filesystem is + quiescent, which is sometimes hard to achieve +*/ +BOOL torture_qfsinfo(int dummy) +{ + struct cli_state *cli; + int i; + BOOL ret = True; + int count; + union smb_fsinfo *s1, *s2; + TALLOC_CTX *mem_ctx; + + if (!torture_open_connection(&cli)) { + return False; + } + + mem_ctx = talloc_init("torture_qfsinfo"); + + /* scan all the levels, pulling the results */ + for (i=0; levels[i].name; i++) { + levels[i].fsinfo.generic.level = levels[i].level; + levels[i].status = smb_raw_fsinfo(cli->tree, mem_ctx, &levels[i].fsinfo); + } + + /* check for completely broken levels */ + for (count=i=0; levels[i].name; i++) { + if (!NT_STATUS_IS_OK(levels[i].status)) { + printf("ERROR: level %s failed - %s\n", levels[i].name, nt_errstr(levels[i].status)); + count++; + } + } + + if (count != 0) { + ret = False; + printf("%d levels failed\n", count); + if (count > 10) { + printf("too many level failures - giving up\n"); + goto done; + } + } + + /* check for correct aliases */ + s1 = find("SIZE_INFO"); + s2 = find("SIZE_INFORMATION"); + if (s1 && s2) { + VAL_EQUAL(size_info, total_alloc_units, size_info, total_alloc_units); + VAL_EQUAL(size_info, avail_alloc_units, size_info, avail_alloc_units); + VAL_EQUAL(size_info, sectors_per_unit, size_info, sectors_per_unit); + VAL_EQUAL(size_info, bytes_per_sector, size_info, bytes_per_sector); + } + + s1 = find("DEVICE_INFO"); + s2 = find("DEVICE_INFORMATION"); + if (s1 && s2) { + VAL_EQUAL(device_info, device_type, device_info, device_type); + VAL_EQUAL(device_info, characteristics, device_info, characteristics); + } + + s1 = find("VOLUME_INFO"); + s2 = find("VOLUME_INFORMATION"); + if (s1 && s2) { + STRUCT_EQUAL(volume_info, create_time, volume_info, create_time); + VAL_EQUAL (volume_info, serial_number, volume_info, serial_number); + STR_EQUAL (volume_info, volume_name.s, volume_info, volume_name.s); + printf("volume_info.volume_name = '%s'\n", s1->volume_info.out.volume_name.s); + } + + s1 = find("ATTRIBUTE_INFO"); + s2 = find("ATTRIBUTE_INFORMATION"); + if (s1 && s2) { + VAL_EQUAL(attribute_info, fs_attr, + attribute_info, fs_attr); + VAL_EQUAL(attribute_info, max_file_component_length, + attribute_info, max_file_component_length); + STR_EQUAL(attribute_info, fs_type.s, attribute_info, fs_type.s); + printf("attribute_info.fs_type = '%s'\n", s1->attribute_info.out.fs_type.s); + } + + /* check for consistent disk sizes */ + s1 = find("DSKATTR"); + s2 = find("ALLOCATION"); + if (s1 && s2) { + double size1, size2; + double scale = s1->dskattr.out.blocks_per_unit * s1->dskattr.out.block_size; + size1 = 1.0 * + s1->dskattr.out.units_total * + s1->dskattr.out.blocks_per_unit * + s1->dskattr.out.block_size / scale; + size2 = 1.0 * + s2->allocation.out.sectors_per_unit * + s2->allocation.out.total_alloc_units * + s2->allocation.out.bytes_per_sector / scale; + if (ABS(size1 - size2) > 1) { + printf("Inconsistent total size in DSKATTR and ALLOCATION - size1=%.0f size2=%.0f\n", + size1, size2); + ret = False; + } + printf("total disk = %.0f MB\n", size1*scale/1.0e6); + } + + /* and for consistent free disk space */ + s1 = find("DSKATTR"); + s2 = find("ALLOCATION"); + if (s1 && s2) { + double size1, size2; + double scale = s1->dskattr.out.blocks_per_unit * s1->dskattr.out.block_size; + size1 = 1.0 * + s1->dskattr.out.units_free * + s1->dskattr.out.blocks_per_unit * + s1->dskattr.out.block_size / scale; + size2 = 1.0 * + s2->allocation.out.sectors_per_unit * + s2->allocation.out.avail_alloc_units * + s2->allocation.out.bytes_per_sector / scale; + if (ABS(size1 - size2) > 1) { + printf("Inconsistent avail size in DSKATTR and ALLOCATION - size1=%.0f size2=%.0f\n", + size1, size2); + ret = False; + } + printf("free disk = %.0f MB\n", size1*scale/1.0e6); + } + + /* volume info consistency */ + s1 = find("VOLUME"); + s2 = find("VOLUME_INFO"); + if (s1 && s2) { + VAL_EQUAL(volume, serial_number, volume_info, serial_number); + STR_EQUAL(volume, volume_name.s, volume_info, volume_name.s); + } + + /* disk size consistency - notice that 'avail_alloc_units' maps to the caller + available allocation units, not the total */ + s1 = find("SIZE_INFO"); + s2 = find("FULL_SIZE_INFORMATION"); + if (s1 && s2) { + VAL_EQUAL(size_info, total_alloc_units, full_size_information, total_alloc_units); + VAL_EQUAL(size_info, avail_alloc_units, full_size_information, call_avail_alloc_units); + VAL_EQUAL(size_info, sectors_per_unit, full_size_information, sectors_per_unit); + VAL_EQUAL(size_info, bytes_per_sector, full_size_information, bytes_per_sector); + } + + /* check for non-zero unknown fields - if we find them + they might give us some hints */ + s1 = find("QUOTA_INFORMATION"); + if (s1) { + VAL_UNKNOWN(quota_information, unknown[0]); + VAL_UNKNOWN(quota_information, unknown[1]); + VAL_UNKNOWN(quota_information, unknown[2]); + } + + s1 = find("OBJECTID_INFORMATION"); + if (s1) { + VAL_UNKNOWN(objectid_information, unknown[0]); + VAL_UNKNOWN(objectid_information, unknown[1]); + VAL_UNKNOWN(objectid_information, unknown[2]); + VAL_UNKNOWN(objectid_information, unknown[3]); + VAL_UNKNOWN(objectid_information, unknown[4]); + VAL_UNKNOWN(objectid_information, unknown[5]); + } + + +#define STR_CHECK(sname, stype, field, flags) do { \ + s1 = find(sname); \ + if (s1) { \ + if (wire_bad_flags(&s1->stype.out.field, flags)) { \ + printf("(%d) incorrect string termination in %s/%s\n", \ + __LINE__, #stype, #field); \ + ret = False; \ + } \ + }} while (0) + + /* check for correct termination */ + STR_CHECK("VOLUME", volume, volume_name, 0); + STR_CHECK("VOLUME_INFO", volume_info, volume_name, STR_UNICODE); + STR_CHECK("VOLUME_INFORMATION", volume_info, volume_name, STR_UNICODE); + STR_CHECK("ATTRIBUTE_INFO", attribute_info, fs_type, STR_UNICODE); + STR_CHECK("ATTRIBUTE_INFORMATION", attribute_info, fs_type, STR_UNICODE); + +done: + torture_close_connection(cli); + talloc_destroy(mem_ctx); + return ret; +} diff --git a/source4/torture/raw/chkpath.c b/source4/torture/raw/chkpath.c new file mode 100644 index 0000000000..3364c39a73 --- /dev/null +++ b/source4/torture/raw/chkpath.c @@ -0,0 +1,142 @@ +/* + Unix SMB/CIFS implementation. + chkpath individual test suite + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 BASEDIR "\\rawchkpath" + +#define CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + printf("(%d) Incorrect status %s - should be %s\n", \ + __LINE__, nt_errstr(status), nt_errstr(correct)); \ + ret = False; \ + goto done; \ + }} while (0) + + +static BOOL test_chkpath(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + struct smb_chkpath io; + NTSTATUS status; + BOOL ret = True; + int fnum = -1; + + io.in.path = BASEDIR; + + status = smb_raw_chkpath(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + io.in.path = BASEDIR "\\nodir"; + status = smb_raw_chkpath(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + fnum = create_complex_file(cli, mem_ctx, BASEDIR "\\test.txt"); + if (fnum == -1) { + printf("failed to open test.txt - %s\n", cli_errstr(cli)); + ret = False; + goto done; + } + + io.in.path = BASEDIR "\\test.txt"; + printf("testing %s\n", io.in.path); + status = smb_raw_chkpath(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_NOT_A_DIRECTORY); + + if (!torture_set_file_attribute(cli->tree, BASEDIR, FILE_ATTRIBUTE_HIDDEN)) { + printf("failed to set basedir hidden\n"); + ret = False; + goto done; + } + + io.in.path = BASEDIR; + printf("testing %s\n", io.in.path); + status = smb_raw_chkpath(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + io.in.path = ""; + printf("testing %s\n", io.in.path); + status = smb_raw_chkpath(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + io.in.path = "."; + printf("testing %s\n", io.in.path); + status = smb_raw_chkpath(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID); + + io.in.path = "\\"; + printf("testing %s\n", io.in.path); + status = smb_raw_chkpath(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + io.in.path = "\\."; + printf("testing %s\n", io.in.path); + status = smb_raw_chkpath(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID); + + io.in.path = "\\.."; + printf("testing %s\n", io.in.path); + status = smb_raw_chkpath(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD); + + io.in.path = BASEDIR "\\.."; + printf("testing %s\n", io.in.path); + status = smb_raw_chkpath(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + +done: + cli_close(cli, fnum); + return ret; +} + +/* + basic testing of chkpath calls +*/ +BOOL torture_raw_chkpath(int dummy) +{ + struct cli_state *cli; + BOOL ret = True; + TALLOC_CTX *mem_ctx; + + if (!torture_open_connection(&cli)) { + return False; + } + + mem_ctx = talloc_init("torture_raw_chkpath"); + + if (cli_deltree(cli, BASEDIR) == -1) { + printf("Failed to clean " BASEDIR "\n"); + return False; + } + if (!cli_mkdir(cli, BASEDIR)) { + printf("Failed to create " BASEDIR " - %s\n", cli_errstr(cli)); + return False; + } + + if (!test_chkpath(cli, mem_ctx)) { + ret = False; + } + + smb_raw_exit(cli->session); + cli_deltree(cli, BASEDIR); + + torture_close_connection(cli); + talloc_destroy(mem_ctx); + return ret; +} diff --git a/source4/torture/raw/close.c b/source4/torture/raw/close.c new file mode 100644 index 0000000000..40bb57f303 --- /dev/null +++ b/source4/torture/raw/close.c @@ -0,0 +1,170 @@ +/* + Unix SMB/CIFS implementation. + RAW_CLOSE_* individual test suite + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + + +/* basic testing of all RAW_CLOSE_* calls +*/ +BOOL torture_raw_close(int dummy) +{ + struct cli_state *cli; + BOOL ret = True; + TALLOC_CTX *mem_ctx; + union smb_close io; + struct smb_flush io_flush; + int fnum; + const char *fname = "\\torture_close.txt"; + time_t basetime = (time(NULL) + 3*86400) & ~1; + union smb_fileinfo finfo, finfo2; + NTSTATUS status; + + if (!torture_open_connection(&cli)) { + return False; + } + + mem_ctx = talloc_init("torture_raw_close"); + +#define REOPEN do { \ + fnum = create_complex_file(cli, mem_ctx, fname); \ + if (fnum == -1) { \ + printf("(%d) Failed to create %s\n", __LINE__, fname); \ + ret = False; \ + goto done; \ + }} while (0) + +#define CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + printf("(%d) Incorrect status %s - should be %s\n", \ + __LINE__, nt_errstr(status), nt_errstr(correct)); \ + ret = False; \ + goto done; \ + }} while (0) + + REOPEN; + + io.close.level = RAW_CLOSE_CLOSE; + io.close.in.fnum = fnum; + io.close.in.write_time = basetime; + status = smb_raw_close(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb_raw_close(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + + printf("testing close.in.write_time\n"); + + /* the file should have the write time set */ + finfo.generic.in.fname = fname; + finfo.generic.level = RAW_FILEINFO_ALL_INFO; + status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + + if (basetime != nt_time_to_unix(&finfo.all_info.out.write_time)) { + printf("Incorrect write time on file - %s - %s\n", + time_string(mem_ctx, basetime), + nt_time_string(mem_ctx, &finfo.all_info.out.write_time)); + dump_all_info(mem_ctx, &finfo); + ret = False; + } + + printf("testing other times\n"); + + /* none of the other times should be set to that time */ + if (nt_time_equal(&finfo.all_info.out.write_time, + &finfo.all_info.out.access_time) || + nt_time_equal(&finfo.all_info.out.write_time, + &finfo.all_info.out.create_time) || + nt_time_equal(&finfo.all_info.out.write_time, + &finfo.all_info.out.change_time)) { + printf("Incorrect times after close - only write time should be set\n"); + dump_all_info(mem_ctx, &finfo); + ret = False; + } + + + cli_unlink(cli, fname); + REOPEN; + + finfo2.generic.in.fname = fname; + finfo2.generic.level = RAW_FILEINFO_ALL_INFO; + status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo2); + CHECK_STATUS(status, NT_STATUS_OK); + + io.close.level = RAW_CLOSE_CLOSE; + io.close.in.fnum = fnum; + io.close.in.write_time = 0; + status = smb_raw_close(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + /* the file should have the write time set equal to access time */ + finfo.generic.in.fname = fname; + finfo.generic.level = RAW_FILEINFO_ALL_INFO; + status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + + if (!nt_time_equal(&finfo.all_info.out.write_time, + &finfo2.all_info.out.write_time)) { + printf("Incorrect write time on file - 0 time should be ignored\n"); + dump_all_info(mem_ctx, &finfo); + ret = False; + } + + printf("testing splclose\n"); + + /* check splclose on a file */ + REOPEN; + io.splclose.level = RAW_CLOSE_SPLCLOSE; + io.splclose.in.fnum = fnum; + status = smb_raw_close(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_UNSUCCESSFUL); + + printf("testing flush\n"); + cli_close(cli, fnum); + + io_flush.in.fnum = fnum; + status = smb_raw_flush(cli->tree, &io_flush); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + + io_flush.in.fnum = 0xffff; + status = smb_raw_flush(cli->tree, &io_flush); + CHECK_STATUS(status, NT_STATUS_OK); + + REOPEN; + + io_flush.in.fnum = fnum; + status = smb_raw_flush(cli->tree, &io_flush); + CHECK_STATUS(status, NT_STATUS_OK); + + printf("Testing SMBexit\n"); + smb_raw_exit(cli->session); + + io_flush.in.fnum = fnum; + status = smb_raw_flush(cli->tree, &io_flush); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + + +done: + cli_close(cli, fnum); + cli_unlink(cli, fname); + torture_close_connection(cli); + talloc_destroy(mem_ctx); + return ret; +} diff --git a/source4/torture/raw/context.c b/source4/torture/raw/context.c new file mode 100644 index 0000000000..c19fea458d --- /dev/null +++ b/source4/torture/raw/context.c @@ -0,0 +1,388 @@ +/* + Unix SMB/CIFS implementation. + test suite for session setup operations + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 BASEDIR "\\rawcontext" + +#define CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + printf("(%d) Incorrect status %s - should be %s\n", \ + __LINE__, nt_errstr(status), nt_errstr(correct)); \ + ret = False; \ + goto done; \ + }} while (0) + +#define CHECK_VALUE(v, correct) do { \ + if ((v) != (correct)) { \ + printf("(%d) Incorrect value %s=%d - should be %d\n", \ + __LINE__, #v, v, correct); \ + ret = False; \ + goto done; \ + }} while (0) + + +/* + test session ops +*/ +static BOOL test_session(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + NTSTATUS status; + BOOL ret = True; + char *username, *domain, *password; + struct cli_session *session; + struct cli_tree *tree; + union smb_sesssetup setup; + union smb_open io; + union smb_write wr; + union smb_close cl; + int fnum; + const char *fname = BASEDIR "\\test.txt"; + char c = 1; + + printf("TESTING SESSION HANDLING\n"); + + if (cli_deltree(cli, BASEDIR) == -1 || + !cli_mkdir(cli, BASEDIR)) { + printf("Unable to setup %s - %s\n", BASEDIR, cli_errstr(cli)); + return False; + } + + username = lp_parm_string(-1, "torture", "username"); + password = lp_parm_string(-1, "torture", "password"); + domain = lp_workgroup(); + + printf("create a second security context on the same transport\n"); + session = cli_session_init(cli->transport); + setup.generic.level = RAW_SESSSETUP_GENERIC; + setup.generic.in.sesskey = cli->transport->negotiate.sesskey; + setup.generic.in.capabilities = 0; /* ignored in secondary session setup */ + setup.generic.in.password = password; + setup.generic.in.user = username; + setup.generic.in.domain = domain; + + status = smb_raw_session_setup(session, mem_ctx, &setup); + CHECK_STATUS(status, NT_STATUS_OK); + + session->vuid = setup.generic.out.vuid; + + printf("use the same tree as the existing connection\n"); + tree = cli_tree_init(session); + tree->tid = cli->tree->tid; + cli->tree->reference_count++; + + printf("vuid1=%d vuid2=%d\n", cli->session->vuid, session->vuid); + + printf("create a file using the new vuid\n"); + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_RIGHT_MAXIMUM_ALLOWED; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + status = smb_raw_open(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.fnum; + + printf("write using the old vuid\n"); + wr.generic.level = RAW_WRITE_WRITEX; + wr.writex.in.fnum = fnum; + wr.writex.in.offset = 0; + wr.writex.in.wmode = 0; + wr.writex.in.remaining = 0; + wr.writex.in.count = 1; + wr.writex.in.data = &c; + + status = smb_raw_write(cli->tree, &wr); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + + printf("write with the new vuid\n"); + status = smb_raw_write(tree, &wr); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(wr.writex.out.nwritten, 1); + + printf("logoff the new vuid\n"); + status = smb_raw_ulogoff(session); + CHECK_STATUS(status, NT_STATUS_OK); + + printf("the new vuid should not now be accessible\n"); + status = smb_raw_write(tree, &wr); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + + printf("the fnum should have been auto-closed\n"); + cl.close.level = RAW_CLOSE_CLOSE; + cl.close.in.fnum = fnum; + cl.close.in.write_time = 0; + status = smb_raw_close(cli->tree, &cl); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + + /* close down the new tree, which will also close the session + as the reference count will be 0 */ + cli_tree_close(tree); + +done: + return ret; +} + + +/* + test tree ops +*/ +static BOOL test_tree(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + NTSTATUS status; + BOOL ret = True; + char *share; + struct cli_tree *tree; + union smb_tcon tcon; + union smb_open io; + union smb_write wr; + union smb_close cl; + int fnum; + const char *fname = BASEDIR "\\test.txt"; + char c = 1; + + printf("TESTING TREE HANDLING\n"); + + if (cli_deltree(cli, BASEDIR) == -1 || + !cli_mkdir(cli, BASEDIR)) { + printf("Unable to setup %s - %s\n", BASEDIR, cli_errstr(cli)); + return False; + } + + share = lp_parm_string(-1, "torture", "share"); + + printf("create a second tree context on the same session\n"); + tree = cli_tree_init(cli->session); + + tcon.generic.level = RAW_TCON_TCONX; + tcon.tconx.in.flags = 0; + tcon.tconx.in.password = data_blob(NULL, 0); + tcon.tconx.in.path = share; + tcon.tconx.in.device = "A:"; + status = smb_tree_connect(tree, mem_ctx, &tcon); + CHECK_STATUS(status, NT_STATUS_OK); + + tree->tid = tcon.tconx.out.cnum; + printf("tid1=%d tid2=%d\n", cli->tree->tid, tree->tid); + + printf("try a tconx with a bad device type\n"); + tcon.tconx.in.device = "FOO"; + status = smb_tree_connect(tree, mem_ctx, &tcon); + CHECK_STATUS(status, NT_STATUS_BAD_DEVICE_TYPE); + + + printf("create a file using the new tid\n"); + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_RIGHT_MAXIMUM_ALLOWED; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + status = smb_raw_open(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.fnum; + + printf("write using the old tid\n"); + wr.generic.level = RAW_WRITE_WRITEX; + wr.writex.in.fnum = fnum; + wr.writex.in.offset = 0; + wr.writex.in.wmode = 0; + wr.writex.in.remaining = 0; + wr.writex.in.count = 1; + wr.writex.in.data = &c; + + status = smb_raw_write(cli->tree, &wr); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + + printf("write with the new tid\n"); + status = smb_raw_write(tree, &wr); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(wr.writex.out.nwritten, 1); + + printf("disconnect the new tid\n"); + status = smb_tree_disconnect(tree); + CHECK_STATUS(status, NT_STATUS_OK); + + printf("the new tid should not now be accessible\n"); + status = smb_raw_write(tree, &wr); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + + printf("the fnum should have been auto-closed\n"); + cl.close.level = RAW_CLOSE_CLOSE; + cl.close.in.fnum = fnum; + cl.close.in.write_time = 0; + status = smb_raw_close(cli->tree, &cl); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + + /* close down the new tree */ + cli_tree_close(tree); + +done: + return ret; +} + + +/* + test pid ops +*/ +static BOOL test_pid(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + NTSTATUS status; + BOOL ret = True; + union smb_open io; + union smb_write wr; + union smb_close cl; + int fnum; + const char *fname = BASEDIR "\\test.txt"; + char c = 1; + uint16 pid1, pid2; + + printf("TESTING PID HANDLING\n"); + + if (cli_deltree(cli, BASEDIR) == -1 || + !cli_mkdir(cli, BASEDIR)) { + printf("Unable to setup %s - %s\n", BASEDIR, cli_errstr(cli)); + return False; + } + + printf("create a second pid\n"); + pid1 = cli->session->pid; + pid2 = pid1+1; + + printf("pid1=%d pid2=%d\n", pid1, pid2); + + printf("create a file using the new pid\n"); + cli->session->pid = pid2; + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_RIGHT_MAXIMUM_ALLOWED; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.fnum; + + printf("write using the old pid\n"); + cli->session->pid = pid1; + wr.generic.level = RAW_WRITE_WRITEX; + wr.writex.in.fnum = fnum; + wr.writex.in.offset = 0; + wr.writex.in.wmode = 0; + wr.writex.in.remaining = 0; + wr.writex.in.count = 1; + wr.writex.in.data = &c; + + status = smb_raw_write(cli->tree, &wr); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(wr.writex.out.nwritten, 1); + + printf("write with the new pid\n"); + cli->session->pid = pid2; + status = smb_raw_write(cli->tree, &wr); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(wr.writex.out.nwritten, 1); + + printf("exit the old pid\n"); + cli->session->pid = pid1; + status = smb_raw_exit(cli->session); + CHECK_STATUS(status, NT_STATUS_OK); + + printf("the fnum should still be accessible\n"); + cli->session->pid = pid1; + status = smb_raw_write(cli->tree, &wr); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(wr.writex.out.nwritten, 1); + + printf("exit the new pid\n"); + cli->session->pid = pid2; + status = smb_raw_exit(cli->session); + CHECK_STATUS(status, NT_STATUS_OK); + + printf("the fnum should not now be accessible\n"); + cli->session->pid = pid1; + status = smb_raw_write(cli->tree, &wr); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + + printf("the fnum should have been auto-closed\n"); + cl.close.level = RAW_CLOSE_CLOSE; + cl.close.in.fnum = fnum; + cl.close.in.write_time = 0; + status = smb_raw_close(cli->tree, &cl); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + +done: + return ret; +} + + +/* + basic testing of session/tree context calls +*/ +BOOL torture_raw_context(int dummy) +{ + struct cli_state *cli; + BOOL ret = True; + TALLOC_CTX *mem_ctx; + + if (!torture_open_connection(&cli)) { + return False; + } + + mem_ctx = talloc_init("torture_raw_context"); + + if (!test_session(cli, mem_ctx)) { + ret = False; + } + + if (!test_tree(cli, mem_ctx)) { + ret = False; + } + + if (!test_pid(cli, mem_ctx)) { + ret = False; + } + + smb_raw_exit(cli->session); + cli_deltree(cli, BASEDIR); + + torture_close_connection(cli); + talloc_destroy(mem_ctx); + return ret; +} diff --git a/source4/torture/raw/ioctl.c b/source4/torture/raw/ioctl.c new file mode 100644 index 0000000000..d55db4c1e6 --- /dev/null +++ b/source4/torture/raw/ioctl.c @@ -0,0 +1,156 @@ +/* + Unix SMB/CIFS implementation. + ioctl individual test suite + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 BASEDIR "\\rawioctl" + +#define CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + printf("(%d) Incorrect status %s - should be %s\n", \ + __LINE__, nt_errstr(status), nt_errstr(correct)); \ + ret = False; \ + goto done; \ + }} while (0) + + +/* test some ioctls */ +static BOOL test_ioctl(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + struct smb_ioctl ctl; + int fnum; + NTSTATUS status; + BOOL ret = True; + const char *fname = BASEDIR "\\test.dat"; + + printf("TESTING IOCTL FUNCTIONS\n"); + + fnum = create_complex_file(cli, mem_ctx, fname); + if (fnum == -1) { + printf("Failed to create test.dat - %s\n", cli_errstr(cli)); + ret = False; + goto done; + } + + printf("Trying QUERY_JOB_INFO\n"); + ctl.in.fnum = fnum; + ctl.in.request = IOCTL_QUERY_JOB_INFO; + + status = smb_raw_ioctl(cli->tree, mem_ctx, &ctl); + CHECK_STATUS(status, NT_STATUS_UNSUCCESSFUL); + + printf("Trying bad handle\n"); + ctl.in.fnum = fnum+1; + status = smb_raw_ioctl(cli->tree, mem_ctx, &ctl); + CHECK_STATUS(status, NT_STATUS_UNSUCCESSFUL); + +done: + cli_close(cli, fnum); + return ret; +} + +/* test some filesystem control functions */ +static BOOL test_fsctl(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + int fnum; + NTSTATUS status; + BOOL ret = True; + const char *fname = BASEDIR "\\test.dat"; + struct smb_ntioctl nt; + + printf("\nTESTING FSCTL FUNCTIONS\n"); + + fnum = create_complex_file(cli, mem_ctx, fname); + if (fnum == -1) { + printf("Failed to create test.dat - %s\n", cli_errstr(cli)); + ret = False; + goto done; + } + + printf("trying sparse file\n"); + nt.in.function = FSCTL_SET_SPARSE; + nt.in.fnum = fnum; + nt.in.fsctl = True; + nt.in.filter = 0; + + status = smb_raw_ntioctl(cli->tree, &nt); + CHECK_STATUS(status, NT_STATUS_OK); + + printf("Trying bad handle\n"); + nt.in.fnum = fnum+1; + status = smb_raw_ntioctl(cli->tree, &nt); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + +#if 0 + nt.in.fnum = fnum; + for (i=0;i<100;i++) { + nt.in.function = FSCTL_FILESYSTEM + (i<<2); + status = smb_raw_ntioctl(cli->tree, &nt); + if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) { + printf("filesystem fsctl 0x%x - %s\n", + i, nt_errstr(status)); + } + } +#endif + +done: + cli_close(cli, fnum); + return ret; +} + +/* + basic testing of some ioctl calls +*/ +BOOL torture_raw_ioctl(int dummy) +{ + struct cli_state *cli; + BOOL ret = True; + TALLOC_CTX *mem_ctx; + + if (!torture_open_connection(&cli)) { + return False; + } + + mem_ctx = talloc_init("torture_raw_ioctl"); + + if (cli_deltree(cli, BASEDIR) == -1) { + printf("Failed to clean " BASEDIR "\n"); + return False; + } + if (!cli_mkdir(cli, BASEDIR)) { + printf("Failed to create " BASEDIR " - %s\n", cli_errstr(cli)); + return False; + } + + if (!test_ioctl(cli, mem_ctx)) { + ret = False; + } + + if (!test_fsctl(cli, mem_ctx)) { + ret = False; + } + + smb_raw_exit(cli->session); + cli_deltree(cli, BASEDIR); + + torture_close_connection(cli); + talloc_destroy(mem_ctx); + return ret; +} diff --git a/source4/torture/raw/lock.c b/source4/torture/raw/lock.c new file mode 100644 index 0000000000..b0b0b56451 --- /dev/null +++ b/source4/torture/raw/lock.c @@ -0,0 +1,216 @@ +/* + Unix SMB/CIFS implementation. + test suite for various lock operations + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + printf("(%d) Incorrect status %s - should be %s\n", \ + __LINE__, nt_errstr(status), nt_errstr(correct)); \ + ret = False; \ + goto done; \ + }} while (0) + +#define CHECK_VALUE(v, correct) do { \ + if ((v) != (correct)) { \ + printf("(%d) Incorrect value %s=%d - should be %d\n", \ + __LINE__, #v, v, correct); \ + ret = False; \ + goto done; \ + }} while (0) + +#define BASEDIR "\\testlock" + + +/* + test SMBlock and SMBunlock ops +*/ +static BOOL test_lock(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + union smb_lock io; + NTSTATUS status; + BOOL ret = True; + int fnum; + const char *fname = BASEDIR "\\test.txt"; + + if (cli_deltree(cli, BASEDIR) == -1 || + !cli_mkdir(cli, BASEDIR)) { + printf("Unable to setup %s - %s\n", BASEDIR, cli_errstr(cli)); + return False; + } + + printf("Testing RAW_LOCK_LOCK\n"); + io.generic.level = RAW_LOCK_LOCK; + + fnum = cli_open(cli, fname, O_RDWR|O_CREAT, DENY_NONE); + if (fnum == -1) { + printf("Failed to create %s - %s\n", fname, cli_errstr(cli)); + ret = False; + goto done; + } + + printf("Trying 0/0 lock\n"); + io.lock.level = RAW_LOCK_LOCK; + io.lock.in.fnum = fnum; + io.lock.in.count = 0; + io.lock.in.offset = 0; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + cli->session->pid++; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + cli->session->pid--; + io.lock.level = RAW_LOCK_UNLOCK; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + printf("Trying 0/1 lock\n"); + io.lock.level = RAW_LOCK_LOCK; + io.lock.in.fnum = fnum; + io.lock.in.count = 1; + io.lock.in.offset = 0; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + cli->session->pid++; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + cli->session->pid--; + io.lock.level = RAW_LOCK_UNLOCK; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + io.lock.level = RAW_LOCK_UNLOCK; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + + printf("Trying max lock\n"); + io.lock.level = RAW_LOCK_LOCK; + io.lock.in.fnum = fnum; + io.lock.in.count = 4000; + io.lock.in.offset = ~0; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + cli->session->pid++; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + cli->session->pid--; + io.lock.level = RAW_LOCK_UNLOCK; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + io.lock.level = RAW_LOCK_UNLOCK; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + + printf("Trying wrong pid unlock\n"); + io.lock.level = RAW_LOCK_LOCK; + io.lock.in.fnum = fnum; + io.lock.in.count = 4002; + io.lock.in.offset = 10001; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + cli->session->pid++; + io.lock.level = RAW_LOCK_UNLOCK; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + cli->session->pid--; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + +done: + smb_raw_exit(cli->session); + cli_deltree(cli, BASEDIR); + return ret; +} + + +/* + test locking&X ops +*/ +static BOOL test_lockx(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + union smb_lock io; + struct smb_lock_entry lock[1]; + NTSTATUS status; + BOOL ret = True; + int fnum; + const char *fname = BASEDIR "\\test.txt"; + + if (cli_deltree(cli, BASEDIR) == -1 || + !cli_mkdir(cli, BASEDIR)) { + printf("Unable to setup %s - %s\n", BASEDIR, cli_errstr(cli)); + return False; + } + + printf("Testing RAW_LOCK_LOCKX\n"); + io.generic.level = RAW_LOCK_LOCKX; + + fnum = cli_open(cli, fname, O_RDWR|O_CREAT, DENY_NONE); + if (fnum == -1) { + printf("Failed to create %s - %s\n", fname, cli_errstr(cli)); + ret = False; + goto done; + } + + io.lockx.level = RAW_LOCK_LOCKX; + io.lockx.in.fnum = fnum; + io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; + io.lockx.in.timeout = 0; + io.lockx.in.ulock_cnt = 0; + io.lockx.in.lock_cnt = 1; + lock[0].pid = cli->session->pid; + lock[0].offset = 0; + lock[0].count = 0xFFFFFFFF; + io.lockx.in.locks = &lock[0]; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + +done: + smb_raw_exit(cli->session); + cli_deltree(cli, BASEDIR); + return ret; +} + + +/* + basic testing of lock calls +*/ +BOOL torture_raw_lock(int dummy) +{ + struct cli_state *cli; + BOOL ret = True; + TALLOC_CTX *mem_ctx; + + if (!torture_open_connection(&cli)) { + return False; + } + + mem_ctx = talloc_init("torture_raw_lock"); + + if (!test_lockx(cli, mem_ctx)) { + ret = False; + } + + if (!test_lock(cli, mem_ctx)) { + ret = False; + } + + torture_close_connection(cli); + talloc_destroy(mem_ctx); + return ret; +} diff --git a/source4/torture/raw/missing.txt b/source4/torture/raw/missing.txt new file mode 100644 index 0000000000..8e026b78ff --- /dev/null +++ b/source4/torture/raw/missing.txt @@ -0,0 +1,157 @@ +- all messaging commands + +- writebraw + +- writebmpx + +- acl ops + +- readbmpx + +- rap commands + +- rpc commands + +- SMBcopy + +- SMBtcon + +- SMBecho + +- SMBfunique + +- SMBsearch vs SMBffirst? + +- SMBfclose + +- SMBkeepalive + +- secondary trans2 and nttrans + +- trans2 ioctl + +- trans2 session setup + +- trans2 DFS ops + +- unix ops + +-------------- +done: + +mkdir +rmdir +open +create +close +flush +unlink +mv +getatr +setatr +read +write +lock +unlock +ctemp +mknew +chkpath +exit +lseek +tconX +tdis +negprot +dskattr +search +lockread +writeunlock +readbraw +setattrE +getattrE +lockingX +ioctl +openX +readX +writeX +sesssetupX +trans2 +findclose +ulogoffX +nttrans +ntcreateX +ntcancel +trans2_open +trans2_findfirst +trans2_findnext? +trans2_qfsinfo +trans2_setfsinfo +trans2_qpathinfo +trans2_setpathinfo +trans2_qfileinfo +trans2_setfileinfo +trans2_fsctl +trans2_mkdir +trans2_findnext +SMB_QFS_ALLOCATION +SMB_QFS_VOLUME +SMB_QFS_VOLUME_INFO +SMB_QFS_SIZE_INFO +SMB_QFS_DEVICE_INFO +SMB_QFS_ATTRIBUTE_INFO +SMB_QFS_VOLUME_INFORMATION +SMB_QFS_SIZE_INFORMATION +SMB_QFS_DEVICE_INFORMATION +SMB_QFS_ATTRIBUTE_INFORMATION +SMB_QFS_QUOTA_INFORMATION +SMB_QFS_FULL_SIZE_INFORMATION +SMB_QFS_OBJECTID_INFORMATION +SMB_QFILEINFO_STANDARD +SMB_QFILEINFO_EA_SIZE +SMB_QFILEINFO_ALL_EAS +SMB_QFILEINFO_IS_NAME_VALID +SMB_QFILEINFO_BASIC_INFO +SMB_QFILEINFO_STANDARD_INFO +SMB_QFILEINFO_EA_INFO +SMB_QFILEINFO_NAME_INFO +SMB_QFILEINFO_ALL_INFO +SMB_QFILEINFO_ALT_NAME_INFO +SMB_QFILEINFO_STREAM_INFO +SMB_QFILEINFO_COMPRESSION_INFO +SMB_QFILEINFO_BASIC_INFORMATION +SMB_QFILEINFO_STANDARD_INFORMATION +SMB_QFILEINFO_INTERNAL_INFORMATION +SMB_QFILEINFO_EA_INFORMATION +SMB_QFILEINFO_ACCESS_INFORMATION +SMB_QFILEINFO_NAME_INFORMATION +SMB_QFILEINFO_POSITION_INFORMATION +SMB_QFILEINFO_MODE_INFORMATION +SMB_QFILEINFO_ALIGNMENT_INFORMATION +SMB_QFILEINFO_ALL_INFORMATION +SMB_QFILEINFO_ALT_NAME_INFORMATION +SMB_QFILEINFO_STREAM_INFORMATION +SMB_QFILEINFO_COMPRESSION_INFORMATION +SMB_QFILEINFO_NETWORK_OPEN_INFORMATION +SMB_QFILEINFO_ATTRIBUTE_TAG_INFORMATION +SMB_SFILEINFO_STANDARD +SMB_SFILEINFO_EA_SET +SMB_SFILEINFO_BASIC_INFO +SMB_SFILEINFO_DISPOSITION_INFO +SMB_SFILEINFO_ALLOCATION_INFO +SMB_SFILEINFO_END_OF_FILE_INFO +SMB_SFILEINFO_UNIX_BASIC +SMB_SFILEINFO_UNIX_LINK +SMB_SFILEINFO_BASIC_INFORMATION +SMB_SFILEINFO_RENAME_INFORMATION +SMB_SFILEINFO_DISPOSITION_INFORMATION +SMB_SFILEINFO_POSITION_INFORMATION +SMB_SFILEINFO_MODE_INFORMATION +SMB_SFILEINFO_ALLOCATION_INFORMATION +SMB_SFILEINFO_END_OF_FILE_INFORMATION +SMB_FIND_STANDARD +SMB_FIND_EA_SIZE +SMB_FIND_DIRECTORY_INFO +SMB_FIND_FULL_DIRECTORY_INFO +SMB_FIND_NAME_INFO +SMB_FIND_BOTH_DIRECTORY_INFO +SMB_FIND_261 +SMB_FIND_262 diff --git a/source4/torture/raw/mkdir.c b/source4/torture/raw/mkdir.c new file mode 100644 index 0000000000..52120f0542 --- /dev/null +++ b/source4/torture/raw/mkdir.c @@ -0,0 +1,141 @@ +/* + Unix SMB/CIFS implementation. + RAW_MKDIR_* and RAW_RMDIR_* individual test suite + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + printf("(%d) Incorrect status %s - should be %s\n", \ + __LINE__, nt_errstr(status), nt_errstr(correct)); \ + ret = False; \ + goto done; \ + }} while (0) + +/* + test mkdir ops +*/ +static BOOL test_mkdir(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + union smb_mkdir md; + struct smb_rmdir rd; + const char *path = "\\test_mkdir.dir"; + NTSTATUS status; + BOOL ret = True; + + /* cleanup */ + cli_rmdir(cli, path); + cli_unlink(cli, path); + + /* + basic mkdir + */ + md.mkdir.level = RAW_MKDIR_MKDIR; + md.mkdir.in.path = path; + + status = smb_raw_mkdir(cli->tree, &md); + CHECK_STATUS(status, NT_STATUS_OK); + + printf("testing mkdir collision\n"); + + /* 2nd create */ + status = smb_raw_mkdir(cli->tree, &md); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_COLLISION); + + /* basic rmdir */ + rd.in.path = path; + status = smb_raw_rmdir(cli->tree, &rd); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb_raw_rmdir(cli->tree, &rd); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + printf("testing mkdir collision with file\n"); + + /* name collision with a file */ + cli_close(cli, create_complex_file(cli, mem_ctx, path)); + status = smb_raw_mkdir(cli->tree, &md); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_COLLISION); + + printf("testing rmdir with file\n"); + + /* delete a file with rmdir */ + status = smb_raw_rmdir(cli->tree, &rd); + CHECK_STATUS(status, NT_STATUS_NOT_A_DIRECTORY); + + cli_unlink(cli, path); + + printf("testing invalid dir\n"); + + /* create an invalid dir */ + md.mkdir.in.path = "..\\..\\.."; + status = smb_raw_mkdir(cli->tree, &md); + CHECK_STATUS(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD); + + printf("testing t2mkdir\n"); + + /* try a t2mkdir - need to work out why this fails! */ + md.t2mkdir.level = RAW_MKDIR_T2MKDIR; + md.t2mkdir.in.path = path; + md.t2mkdir.in.num_eas = 0; + status = smb_raw_mkdir(cli->tree, &md); + CHECK_STATUS(status, NT_STATUS_UNSUCCESSFUL); + + printf("testing t2mkdir with EAs\n"); + + /* with EAs */ + md.t2mkdir.in.num_eas = 1; + md.t2mkdir.in.eas = talloc(mem_ctx, sizeof(md.t2mkdir.in.eas[0])); + md.t2mkdir.in.eas[0].flags = 0; + md.t2mkdir.in.eas[0].name.s = "EAONE"; + md.t2mkdir.in.eas[0].value = data_blob_talloc(mem_ctx, "1", 1); + status = smb_raw_mkdir(cli->tree, &md); + CHECK_STATUS(status, NT_STATUS_UNSUCCESSFUL); + + +done: + cli_rmdir(cli, path); + cli_unlink(cli, path); + return ret; +} + + +/* + basic testing of all RAW_MKDIR_* calls +*/ +BOOL torture_raw_mkdir(int dummy) +{ + struct cli_state *cli; + BOOL ret = True; + TALLOC_CTX *mem_ctx; + + if (!torture_open_connection(&cli)) { + return False; + } + + mem_ctx = talloc_init("torture_raw_mkdir"); + + if (!test_mkdir(cli, mem_ctx)) { + ret = False; + } + + torture_close_connection(cli); + talloc_destroy(mem_ctx); + return ret; +} diff --git a/source4/torture/raw/mux.c b/source4/torture/raw/mux.c new file mode 100644 index 0000000000..05c5e3de09 --- /dev/null +++ b/source4/torture/raw/mux.c @@ -0,0 +1,298 @@ +/* + Unix SMB/CIFS implementation. + basic raw test suite for multiplexing + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 BASEDIR "\\test_mux" + +#define CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + printf("(%d) Incorrect status %s - should be %s\n", \ + __LINE__, nt_errstr(status), nt_errstr(correct)); \ + ret = False; \ + goto done; \ + }} while (0) + + +/* + test the delayed reply to a open that leads to a sharing violation +*/ +static BOOL test_mux_open(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + union smb_open io; + NTSTATUS status; + int fnum; + BOOL ret = True; + struct cli_request *req; + + printf("testing multiplexed open/open/close\n"); + + /* + file open with no share access + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SEC_RIGHT_MAXIMUM_ALLOWED; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = 0; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = BASEDIR "\\open.dat"; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.fnum; + + /* send an open that will conflict */ + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION); + + /* + same request, but async + */ + req = smb_raw_open_send(cli->tree, &io); + + /* and close the file */ + cli_close(cli, fnum); + + /* see if the async open succeeded */ + status = smb_raw_open_recv(req, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + cli_close(cli, io.ntcreatex.out.fnum); + +done: + return ret; +} + + +/* + test a write that hits a byte range lock and send the close after the write +*/ +static BOOL test_mux_write(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + union smb_write io; + NTSTATUS status; + int fnum; + BOOL ret = True; + struct cli_request *req; + + printf("testing multiplexed lock/write/close\n"); + + fnum = cli_open(cli, BASEDIR "\\write.dat", O_RDWR | O_CREAT, DENY_NONE); + if (fnum == -1) { + printf("open failed in mux_write - %s\n", cli_errstr(cli)); + ret = False; + goto done; + } + + cli->session->pid = 1; + + /* lock a range */ + if (!cli_lock(cli, fnum, 0, 4, 0, WRITE_LOCK)) { + printf("lock failed in mux_write - %s\n", cli_errstr(cli)); + ret = False; + goto done; + } + + cli->session->pid = 2; + + /* send an async write */ + io.generic.level = RAW_WRITE_WRITEX; + io.writex.in.fnum = fnum; + io.writex.in.offset = 0; + io.writex.in.wmode = 0; + io.writex.in.remaining = 0; + io.writex.in.count = 4; + io.writex.in.data = (void *)&fnum; + req = smb_raw_write_send(cli->tree, &io); + + /* unlock the range */ + cli->session->pid = 1; + cli_unlock(cli, fnum, 0, 4); + + /* and recv the async write reply */ + status = smb_raw_write_recv(req, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + cli_close(cli, fnum); + +done: + return ret; +} + + +/* + test a lock that conflicts with an existing lock +*/ +static BOOL test_mux_lock(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + union smb_lock io; + NTSTATUS status; + int fnum; + BOOL ret = True; + struct cli_request *req; + struct smb_lock_entry lock[1]; + + printf("TESTING MULTIPLEXED LOCK/LOCK/UNLOCK\n"); + + fnum = cli_open(cli, BASEDIR "\\write.dat", O_RDWR | O_CREAT, DENY_NONE); + if (fnum == -1) { + printf("open failed in mux_write - %s\n", cli_errstr(cli)); + ret = False; + goto done; + } + + printf("establishing a lock\n"); + io.lockx.level = RAW_LOCK_LOCKX; + io.lockx.in.fnum = fnum; + io.lockx.in.mode = 0; + io.lockx.in.timeout = 0; + io.lockx.in.lock_cnt = 1; + io.lockx.in.ulock_cnt = 0; + lock[0].pid = 1; + lock[0].offset = 0; + lock[0].count = 4; + io.lockx.in.locks = &lock[0]; + + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + printf("the second lock will conflict with the first\n"); + lock[0].pid = 2; + io.lockx.in.timeout = 1000; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + printf("this will too, but we'll unlock while waiting\n"); + req = smb_raw_lock_send(cli->tree, &io); + + printf("unlock the first range\n"); + lock[0].pid = 1; + io.lockx.in.ulock_cnt = 1; + io.lockx.in.lock_cnt = 0; + io.lockx.in.timeout = 0; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + printf("recv the async reply\n"); + status = cli_request_simple_recv(req); + CHECK_STATUS(status, NT_STATUS_OK); + + printf("reopening with an exit\n"); + smb_raw_exit(cli->session); + fnum = cli_open(cli, BASEDIR "\\write.dat", O_RDWR | O_CREAT, DENY_NONE); + + printf("Now trying with a cancel\n"); + + io.lockx.level = RAW_LOCK_LOCKX; + io.lockx.in.fnum = fnum; + io.lockx.in.mode = 0; + io.lockx.in.timeout = 0; + io.lockx.in.lock_cnt = 1; + io.lockx.in.ulock_cnt = 0; + lock[0].pid = 1; + lock[0].offset = 0; + lock[0].count = 4; + io.lockx.in.locks = &lock[0]; + + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + lock[0].pid = 2; + io.lockx.in.timeout = 1000; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + req = smb_raw_lock_send(cli->tree, &io); + + /* cancel the blocking lock */ + smb_raw_ntcancel(req); + + lock[0].pid = 1; + io.lockx.in.ulock_cnt = 1; + io.lockx.in.lock_cnt = 0; + io.lockx.in.timeout = 0; + status = smb_raw_lock(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + status = cli_request_simple_recv(req); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + cli_close(cli, fnum); + +done: + return ret; +} + + + +/* + basic testing of multiplexing notify +*/ +BOOL torture_raw_mux(int dummy) +{ + struct cli_state *cli; + BOOL ret = True; + TALLOC_CTX *mem_ctx; + + if (!torture_open_connection(&cli)) { + return False; + } + + mem_ctx = talloc_init("torture_raw_mux"); + + /* cleanup */ + if (cli_deltree(cli, BASEDIR) == -1) { + printf("Failed to cleanup " BASEDIR "\n"); + ret = False; + goto done; + } + + + if (!cli_mkdir(cli, BASEDIR)) { + printf("Failed to create %s\n", BASEDIR); + ret = False; + goto done; + } + + if (!test_mux_open(cli, mem_ctx)) { + ret = False; + } + + if (!test_mux_write(cli, mem_ctx)) { + ret = False; + } + + if (!test_mux_lock(cli, mem_ctx)) { + ret = False; + } + +done: + smb_raw_exit(cli->session); + cli_deltree(cli, BASEDIR); + torture_close_connection(cli); + talloc_destroy(mem_ctx); + return ret; +} diff --git a/source4/torture/raw/notify.c b/source4/torture/raw/notify.c new file mode 100644 index 0000000000..a123dc6702 --- /dev/null +++ b/source4/torture/raw/notify.c @@ -0,0 +1,140 @@ +/* + Unix SMB/CIFS implementation. + basic raw test suite for change notify + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 BASEDIR "\\test_notify" + +#define CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + printf("(%d) Incorrect status %s - should be %s\n", \ + __LINE__, nt_errstr(status), nt_errstr(correct)); \ + ret = False; \ + goto done; \ + }} while (0) + + +#define CHECK_VAL(v, correct) do { \ + if ((v) != (correct)) { \ + printf("(%d) wrong value for %s 0x%x - 0x%x\n", \ + __LINE__, #v, (int)v, (int)correct); \ + ret = False; \ + goto done; \ + }} while (0) + +#define CHECK_WSTR(field, value, flags) do { \ + if (!field.s || strcmp(field.s, value) || wire_bad_flags(&field, flags)) { \ + printf("(%d) %s [%s] != %s\n", __LINE__, #field, field.s, value); \ + ret = False; \ + goto done; \ + }} while (0) + + +/* + basic testing of change notify +*/ +BOOL torture_raw_notify(int dummy) +{ + struct cli_state *cli; + BOOL ret = True; + TALLOC_CTX *mem_ctx; + NTSTATUS status; + struct smb_notify notify; + union smb_open io; + int fnum = -1; + struct cli_request *req; + + if (!torture_open_connection(&cli)) { + return False; + } + + mem_ctx = talloc_init("torture_raw_notify"); + + /* cleanup */ + if (cli_deltree(cli, BASEDIR) == -1) { + printf("Failed to cleanup " BASEDIR "\n"); + ret = False; + goto done; + } + + /* + get a handle on the directory + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid = 0; + io.ntcreatex.in.flags = 0; + io.ntcreatex.in.access_mask = SA_RIGHT_FILE_ALL_ACCESS; + io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = BASEDIR; + + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.fnum; + + /* ask for a change notify */ + notify.in.buffer_size = 4096; + notify.in.completion_filter = 0xFF; + notify.in.fnum = fnum; + notify.in.recursive = True; + + printf("testing notify mkdir\n"); + + req = smb_raw_changenotify_send(cli->tree, ¬ify); + cli_mkdir(cli, BASEDIR "\\subdir-name"); + + status = smb_raw_changenotify_recv(req, mem_ctx, ¬ify); + CHECK_STATUS(status, NT_STATUS_OK); + + CHECK_VAL(notify.out.num_changes, 1); + CHECK_VAL(notify.out.changes[0].action, NOTIFY_ACTION_ADDED); + CHECK_WSTR(notify.out.changes[0].name, "subdir-name", STR_UNICODE); + + printf("testing notify rmdir\n"); + + req = smb_raw_changenotify_send(cli->tree, ¬ify); + cli_rmdir(cli, BASEDIR "\\subdir-name"); + + status = smb_raw_changenotify_recv(req, mem_ctx, ¬ify); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(notify.out.num_changes, 1); + CHECK_VAL(notify.out.changes[0].action, NOTIFY_ACTION_REMOVED); + CHECK_WSTR(notify.out.changes[0].name, "subdir-name", STR_UNICODE); + + printf("testing notify cancel\n"); + + req = smb_raw_changenotify_send(cli->tree, ¬ify); + smb_raw_ntcancel(req); + cli_mkdir(cli, BASEDIR "\\subdir-name"); + status = smb_raw_changenotify_recv(req, mem_ctx, ¬ify); + CHECK_STATUS(status, NT_STATUS_CANCELLED); + +done: + smb_raw_exit(cli->session); + cli_deltree(cli, BASEDIR); + torture_close_connection(cli); + talloc_destroy(mem_ctx); + return ret; +} diff --git a/source4/torture/raw/open.c b/source4/torture/raw/open.c new file mode 100644 index 0000000000..9f77eb3f8e --- /dev/null +++ b/source4/torture/raw/open.c @@ -0,0 +1,895 @@ +/* + Unix SMB/CIFS implementation. + RAW_OPEN_* individual test suite + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +/* enum for whether reads/writes are possible on a file */ +enum rdwr_mode {RDWR_NONE, RDWR_RDONLY, RDWR_WRONLY, RDWR_RDWR}; + +#define BASEDIR "\\rawopen" + +/* + check if a open file can be read/written +*/ +static enum rdwr_mode check_rdwr(struct cli_state *cli, int fnum) +{ + char c = 1; + BOOL can_read = (cli_read(cli, fnum, &c, 0, 1) == 1); + BOOL can_write = (cli_write(cli, fnum, 0, &c, 0, 1) == 1); + if ( can_read && can_write) return RDWR_RDWR; + if ( can_read && !can_write) return RDWR_RDONLY; + if (!can_read && can_write) return RDWR_WRONLY; + return RDWR_NONE; +} + +/* + describe a RDWR mode as a string +*/ +static const char *rdwr_string(enum rdwr_mode m) +{ + switch (m) { + case RDWR_NONE: return "NONE"; + case RDWR_RDONLY: return "RDONLY"; + case RDWR_WRONLY: return "WRONLY"; + case RDWR_RDWR: return "RDWR"; + } + return "-"; +} + +#define CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + printf("(%d) Incorrect status %s - should be %s\n", \ + __LINE__, nt_errstr(status), nt_errstr(correct)); \ + ret = False; \ + goto done; \ + }} while (0) + +#define CREATE_FILE do { \ + fnum = create_complex_file(cli, mem_ctx, fname); \ + if (fnum == -1) { \ + printf("(%d) Failed to create %s - %s\n", __LINE__, fname, cli_errstr(cli)); \ + ret = False; \ + goto done; \ + }} while (0) + +#define CHECK_RDWR(fnum, correct) do { \ + enum rdwr_mode m = check_rdwr(cli, fnum); \ + if (m != correct) { \ + printf("(%d) Incorrect readwrite mode %s - expected %s\n", \ + __LINE__, rdwr_string(m), rdwr_string(correct)); \ + ret = False; \ + }} while (0) + +#define CHECK_TIME(t, field) do { \ + time_t t1, t2; \ + finfo.all_info.level = RAW_FILEINFO_ALL_INFO; \ + finfo.all_info.in.fname = fname; \ + status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo); \ + CHECK_STATUS(status, NT_STATUS_OK); \ + t1 = t & ~1; \ + t2 = nt_time_to_unix(&finfo.all_info.out.field) & ~1; \ + if (ABS(t1-t2) > 2) { \ + printf("(%d) wrong time for field %s %s - %s\n", \ + __LINE__, #field, \ + time_string(mem_ctx, t1), \ + time_string(mem_ctx, t2)); \ + dump_all_info(mem_ctx, &finfo); \ + ret = False; \ + }} while (0) + +#define CHECK_NTTIME(t, field) do { \ + NTTIME t2; \ + finfo.all_info.level = RAW_FILEINFO_ALL_INFO; \ + finfo.all_info.in.fname = fname; \ + status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo); \ + CHECK_STATUS(status, NT_STATUS_OK); \ + t2 = finfo.all_info.out.field; \ + if (!nt_time_equal(&t, &t2)) { \ + printf("(%d) wrong time for field %s %s - %s\n", \ + __LINE__, #field, \ + nt_time_string(mem_ctx, &t), \ + nt_time_string(mem_ctx, &t2)); \ + dump_all_info(mem_ctx, &finfo); \ + ret = False; \ + }} while (0) + +#define CHECK_ALL_INFO(v, field) do { \ + finfo.all_info.level = RAW_FILEINFO_ALL_INFO; \ + finfo.all_info.in.fname = fname; \ + status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo); \ + CHECK_STATUS(status, NT_STATUS_OK); \ + if ((v) != finfo.all_info.out.field) { \ + printf("(%d) wrong value for field %s 0x%x - 0x%x\n", \ + __LINE__, #field, (int)v, (int)finfo.all_info.out.field); \ + dump_all_info(mem_ctx, &finfo); \ + ret = False; \ + }} while (0) + +#define CHECK_VAL(v, correct) do { \ + if ((v) != (correct)) { \ + printf("(%d) wrong value for %s 0x%x - 0x%x\n", \ + __LINE__, #v, (int)v, (int)correct); \ + ret = False; \ + }} while (0) + +#define SET_ATTRIB(sattrib) do { \ + union smb_setfileinfo sfinfo; \ + sfinfo.generic.level = RAW_SFILEINFO_BASIC_INFORMATION; \ + sfinfo.generic.file.fname = fname; \ + ZERO_STRUCT(sfinfo.basic_info.in); \ + sfinfo.basic_info.in.attrib = sattrib; \ + status = smb_raw_setpathinfo(cli->tree, &sfinfo); \ + if (!NT_STATUS_IS_OK(status)) { \ + printf("(%d) Failed to set attrib 0x%x on %s\n", \ + __LINE__, sattrib, fname); \ + }} while (0) + +/* + test RAW_OPEN_OPEN +*/ +static BOOL test_open(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + union smb_open io; + union smb_fileinfo finfo; + const char *fname = BASEDIR "\\torture_open.txt"; + NTSTATUS status; + int fnum, fnum2; + BOOL ret = True; + + printf("Checking RAW_OPEN_OPEN\n"); + + io.open.level = RAW_OPEN_OPEN; + io.open.in.fname = fname; + io.open.in.flags = OPEN_FLAGS_FCB; + io.open.in.search_attrs = 0; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + fnum = io.open.out.fnum; + + cli_unlink(cli, fname); + CREATE_FILE; + cli_close(cli, fnum); + + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.open.out.fnum; + CHECK_RDWR(fnum, RDWR_RDWR); + + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum2 = io.open.out.fnum; + CHECK_RDWR(fnum2, RDWR_RDWR); + cli_close(cli, fnum2); + cli_close(cli, fnum); + + /* check the read/write modes */ + io.open.level = RAW_OPEN_OPEN; + io.open.in.fname = fname; + io.open.in.search_attrs = 0; + + io.open.in.flags = OPEN_FLAGS_OPEN_READ; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.open.out.fnum; + CHECK_RDWR(fnum, RDWR_RDONLY); + cli_close(cli, fnum); + + io.open.in.flags = OPEN_FLAGS_OPEN_WRITE; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.open.out.fnum; + CHECK_RDWR(fnum, RDWR_WRONLY); + cli_close(cli, fnum); + + io.open.in.flags = OPEN_FLAGS_OPEN_RDWR; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.open.out.fnum; + CHECK_RDWR(fnum, RDWR_RDWR); + cli_close(cli, fnum); + + /* check the share modes roughly - not a complete matrix */ + io.open.in.flags = OPEN_FLAGS_OPEN_RDWR | OPEN_FLAGS_DENY_WRITE; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.open.out.fnum; + CHECK_RDWR(fnum, RDWR_RDWR); + + if (io.open.in.flags != io.open.out.rmode) { + printf("(%d) rmode should equal flags - 0x%x 0x%x\n", + __LINE__, io.open.out.rmode, io.open.in.flags); + } + + io.open.in.flags = OPEN_FLAGS_OPEN_RDWR | OPEN_FLAGS_DENY_NONE; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION); + + io.open.in.flags = OPEN_FLAGS_OPEN_READ | OPEN_FLAGS_DENY_NONE; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum2 = io.open.out.fnum; + CHECK_RDWR(fnum2, RDWR_RDONLY); + cli_close(cli, fnum); + cli_close(cli, fnum2); + + + /* check the returned write time */ + io.open.level = RAW_OPEN_OPEN; + io.open.in.fname = fname; + io.open.in.search_attrs = 0; + io.open.in.flags = OPEN_FLAGS_OPEN_READ; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.open.out.fnum; + + /* check other reply fields */ + CHECK_TIME(io.open.out.write_time, write_time); + CHECK_ALL_INFO(io.open.out.size, size); + CHECK_ALL_INFO(io.open.out.attrib, attrib); + +done: + cli_close(cli, fnum); + cli_unlink(cli, fname); + + return ret; +} + + +/* + test RAW_OPEN_OPENX +*/ +static BOOL test_openx(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + union smb_open io; + union smb_fileinfo finfo; + const char *fname = BASEDIR "\\torture_openx.txt"; + NTSTATUS status; + int fnum, fnum2; + BOOL ret = True; + int i; + struct { + uint16 open_func; + BOOL with_file; + NTSTATUS correct_status; + } open_funcs[] = { + { OPENX_OPEN_FUNC_OPEN, True, NT_STATUS_OK }, + { OPENX_OPEN_FUNC_OPEN, False, NT_STATUS_OBJECT_NAME_NOT_FOUND }, + { OPENX_OPEN_FUNC_OPEN | OPENX_OPEN_FUNC_CREATE, True, NT_STATUS_OK }, + { OPENX_OPEN_FUNC_OPEN | OPENX_OPEN_FUNC_CREATE, False, NT_STATUS_OK }, + { OPENX_OPEN_FUNC_FAIL, True, NT_STATUS_INVALID_LOCK_SEQUENCE }, + { OPENX_OPEN_FUNC_FAIL, False, NT_STATUS_INVALID_LOCK_SEQUENCE }, + { OPENX_OPEN_FUNC_FAIL | OPENX_OPEN_FUNC_CREATE, True, NT_STATUS_OBJECT_NAME_COLLISION }, + { OPENX_OPEN_FUNC_FAIL | OPENX_OPEN_FUNC_CREATE, False, NT_STATUS_OK }, + { OPENX_OPEN_FUNC_TRUNC, True, NT_STATUS_OK }, + { OPENX_OPEN_FUNC_TRUNC, False, NT_STATUS_OBJECT_NAME_NOT_FOUND }, + { OPENX_OPEN_FUNC_TRUNC | OPENX_OPEN_FUNC_CREATE, True, NT_STATUS_OK }, + { OPENX_OPEN_FUNC_TRUNC | OPENX_OPEN_FUNC_CREATE, False, NT_STATUS_OK }, + }; + + printf("Checking RAW_OPEN_OPENX\n"); + cli_unlink(cli, fname); + + io.openx.level = RAW_OPEN_OPENX; + io.openx.in.fname = fname; + io.openx.in.flags = OPENX_FLAGS_ADDITIONAL_INFO; + io.openx.in.open_mode = OPENX_MODE_ACCESS_RDWR; + io.openx.in.search_attrs = 0; + io.openx.in.file_attrs = 0; + io.openx.in.write_time = 0; + io.openx.in.size = 1024*1024; + io.openx.in.timeout = 0; + + /* check all combinations of open_func */ + for (i=0; itree, mem_ctx, &io); + if (!NT_STATUS_EQUAL(status, open_funcs[i].correct_status)) { + printf("(%d) incorrect status %s should be %s (i=%d with_file=%d open_func=0x%x)\n", + __LINE__, nt_errstr(status), nt_errstr(open_funcs[i].correct_status), + i, (int)open_funcs[i].with_file, (int)open_funcs[i].open_func); + ret = False; + } + if (NT_STATUS_IS_OK(status) || open_funcs[i].with_file) { + cli_close(cli, io.openx.out.fnum); + cli_unlink(cli, fname); + } + } + + /* check the basic return fields */ + io.openx.in.open_func = OPENX_OPEN_FUNC_OPEN | OPENX_OPEN_FUNC_CREATE; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.openx.out.fnum; + + CHECK_ALL_INFO(io.openx.out.size, size); + CHECK_VAL(io.openx.out.size, 1024*1024); + CHECK_ALL_INFO(io.openx.in.size, size); + CHECK_TIME(io.openx.out.write_time, write_time); + CHECK_ALL_INFO(io.openx.out.attrib, attrib); + CHECK_VAL(io.openx.out.access, OPENX_MODE_ACCESS_RDWR); + CHECK_VAL(io.openx.out.ftype, 0); + CHECK_VAL(io.openx.out.devstate, 0); + CHECK_VAL(io.openx.out.action, OPENX_ACTION_CREATED); + cli_close(cli, fnum); + cli_unlink(cli, fname); + + /* check the fields when the file already existed */ + fnum2 = create_complex_file(cli, mem_ctx, fname); + if (fnum2 == -1) { + ret = False; + goto done; + } + cli_close(cli, fnum2); + + io.openx.in.open_func = OPENX_OPEN_FUNC_OPEN; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.openx.out.fnum; + + CHECK_ALL_INFO(io.openx.out.size, size); + CHECK_TIME(io.openx.out.write_time, write_time); + CHECK_VAL(io.openx.out.action, OPENX_ACTION_EXISTED); + CHECK_VAL(io.openx.out.unknown, 0); + CHECK_ALL_INFO(io.openx.out.attrib, attrib); + cli_close(cli, fnum); + + /* now check the search attrib for hidden files - win2003 ignores this? */ + SET_ATTRIB(FILE_ATTRIBUTE_HIDDEN); + CHECK_ALL_INFO(FILE_ATTRIBUTE_HIDDEN, attrib); + + io.openx.in.search_attrs = FILE_ATTRIBUTE_HIDDEN; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + cli_close(cli, io.openx.out.fnum); + + io.openx.in.search_attrs = 0; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + cli_close(cli, io.openx.out.fnum); + + SET_ATTRIB(FILE_ATTRIBUTE_NORMAL); + cli_unlink(cli, fname); + + /* and check attrib on create */ + io.openx.in.open_func = OPENX_OPEN_FUNC_FAIL | OPENX_OPEN_FUNC_CREATE; + io.openx.in.search_attrs = 0; + io.openx.in.file_attrs = FILE_ATTRIBUTE_SYSTEM; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_ALL_INFO(FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE, attrib); + cli_close(cli, io.openx.out.fnum); + cli_unlink(cli, fname); + + /* check timeout on create - win2003 ignores the timeout! */ + io.openx.in.open_func = OPENX_OPEN_FUNC_OPEN | OPENX_OPEN_FUNC_CREATE; + io.openx.in.file_attrs = 0; + io.openx.in.open_mode = OPENX_MODE_ACCESS_RDWR | OPENX_MODE_DENY_ALL; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.openx.out.fnum; + + io.openx.in.timeout = 20000; + start_timer(); + io.openx.in.open_mode = OPENX_MODE_ACCESS_RDWR | OPENX_MODE_DENY_NONE; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION); + if (end_timer() > 3) { + printf("(%d) Incorrect timing in openx with timeout - waited %d seconds\n", + __LINE__, (int)end_timer()); + ret = False; + } + cli_close(cli, fnum); + cli_unlink(cli, fname); + + /* now this is a really weird one - open for execute implies create?! */ + io.openx.in.fname = fname; + io.openx.in.flags = OPENX_FLAGS_ADDITIONAL_INFO; + io.openx.in.open_mode = OPENX_MODE_ACCESS_EXEC | OPENX_MODE_DENY_NONE; + io.openx.in.search_attrs = 0; + io.openx.in.open_func = OPENX_OPEN_FUNC_FAIL; + io.openx.in.file_attrs = 0; + io.openx.in.write_time = 0; + io.openx.in.size = 0; + io.openx.in.timeout = 0; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + cli_close(cli, io.openx.out.fnum); + + /* check the extended return flag */ + io.openx.in.flags = OPENX_FLAGS_ADDITIONAL_INFO | OPENX_FLAGS_EXTENDED_RETURN; + io.openx.in.open_func = OPENX_OPEN_FUNC_OPEN; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(io.openx.out.access_mask, STD_RIGHT_ALL_ACCESS); + cli_close(cli, io.openx.out.fnum); + +done: + cli_close(cli, fnum); + cli_unlink(cli, fname); + + return ret; +} + + +/* + test RAW_OPEN_T2OPEN + + I can't work out how to get win2003 to accept a create file via TRANS2_OPEN, which + is why you see all the ACCESS_DENIED results below. When we finally work this out then this + test will make more sense +*/ +static BOOL test_t2open(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + union smb_open io; + union smb_fileinfo finfo; + const char *fname = BASEDIR "\\torture_t2open.txt"; + NTSTATUS status; + int fnum; + BOOL ret = True; + int i; + struct { + uint16 open_func; + BOOL with_file; + NTSTATUS correct_status; + } open_funcs[] = { + { OPENX_OPEN_FUNC_OPEN, True, NT_STATUS_OK }, + { OPENX_OPEN_FUNC_OPEN, False, NT_STATUS_OBJECT_NAME_NOT_FOUND }, + { OPENX_OPEN_FUNC_OPEN | OPENX_OPEN_FUNC_CREATE, True, NT_STATUS_OK }, + { OPENX_OPEN_FUNC_OPEN | OPENX_OPEN_FUNC_CREATE, False, NT_STATUS_ACCESS_DENIED }, + { OPENX_OPEN_FUNC_FAIL, True, NT_STATUS_OBJECT_NAME_COLLISION }, + { OPENX_OPEN_FUNC_FAIL, False, NT_STATUS_ACCESS_DENIED }, + { OPENX_OPEN_FUNC_FAIL | OPENX_OPEN_FUNC_CREATE, True, NT_STATUS_OBJECT_NAME_COLLISION }, + { OPENX_OPEN_FUNC_FAIL | OPENX_OPEN_FUNC_CREATE, False, NT_STATUS_ACCESS_DENIED }, + { OPENX_OPEN_FUNC_TRUNC, True, NT_STATUS_ACCESS_DENIED }, + { OPENX_OPEN_FUNC_TRUNC, False, NT_STATUS_OBJECT_NAME_NOT_FOUND }, + { OPENX_OPEN_FUNC_TRUNC | OPENX_OPEN_FUNC_CREATE, True, NT_STATUS_ACCESS_DENIED }, + { OPENX_OPEN_FUNC_TRUNC | OPENX_OPEN_FUNC_CREATE, False, NT_STATUS_ACCESS_DENIED }, + }; + + printf("Checking RAW_OPEN_T2OPEN\n"); + + io.t2open.level = RAW_OPEN_T2OPEN; + io.t2open.in.fname = fname; + io.t2open.in.flags = OPENX_FLAGS_ADDITIONAL_INFO | + OPENX_FLAGS_EA_LEN | OPENX_FLAGS_EXTENDED_RETURN; + io.t2open.in.open_mode = OPENX_MODE_DENY_NONE | OPENX_MODE_ACCESS_RDWR; + io.t2open.in.open_func = OPENX_OPEN_FUNC_OPEN | OPENX_OPEN_FUNC_CREATE; + io.t2open.in.file_attrs = 0; + io.t2open.in.write_time = 0; + io.t2open.in.size = 0; + io.t2open.in.timeout = 0; + + io.t2open.in.eas = talloc(mem_ctx, sizeof(io.t2open.in.eas[0])); + io.t2open.in.num_eas = 1; + io.t2open.in.eas[0].flags = 0; + io.t2open.in.eas[0].name.s = "EAONE"; + io.t2open.in.eas[0].value = data_blob_talloc(mem_ctx, "1", 1); + + /* check all combinations of open_func */ + for (i=0; itree, mem_ctx, &io); + if (!NT_STATUS_EQUAL(status, open_funcs[i].correct_status)) { + printf("(%d) incorrect status %s should be %s (i=%d with_file=%d open_func=0x%x)\n", + __LINE__, nt_errstr(status), nt_errstr(open_funcs[i].correct_status), + i, (int)open_funcs[i].with_file, (int)open_funcs[i].open_func); + ret = False; + } + if (NT_STATUS_IS_OK(status) || open_funcs[i].with_file) { + cli_close(cli, io.t2open.out.fnum); + cli_unlink(cli, fname); + } + } + + /* check the basic return fields */ + fnum = create_complex_file(cli, mem_ctx, fname); + cli_close(cli, fnum); + io.t2open.in.open_func = OPENX_OPEN_FUNC_OPEN | OPENX_OPEN_FUNC_CREATE; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.t2open.out.fnum; + + CHECK_ALL_INFO(io.t2open.out.size, size); + CHECK_VAL(io.t2open.out.write_time, 0); + CHECK_ALL_INFO(io.t2open.out.attrib, attrib); + CHECK_VAL(io.t2open.out.access, OPENX_MODE_DENY_NONE | OPENX_MODE_ACCESS_RDWR); + CHECK_VAL(io.t2open.out.ftype, 0); + CHECK_VAL(io.t2open.out.devstate, 0); + CHECK_VAL(io.t2open.out.action, OPENX_ACTION_EXISTED); + cli_close(cli, fnum); + + /* now check the search attrib for hidden files - win2003 ignores this? */ + SET_ATTRIB(FILE_ATTRIBUTE_HIDDEN); + CHECK_ALL_INFO(FILE_ATTRIBUTE_HIDDEN, attrib); + + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + cli_close(cli, io.t2open.out.fnum); + + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + cli_close(cli, io.t2open.out.fnum); + + SET_ATTRIB(FILE_ATTRIBUTE_NORMAL); + cli_unlink(cli, fname); + + /* and check attrib on create */ + io.t2open.in.open_func = OPENX_OPEN_FUNC_FAIL | OPENX_OPEN_FUNC_CREATE; + io.t2open.in.file_attrs = FILE_ATTRIBUTE_SYSTEM; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + + /* check timeout on create - win2003 ignores the timeout! */ + io.t2open.in.open_func = OPENX_OPEN_FUNC_OPEN | OPENX_OPEN_FUNC_CREATE; + io.t2open.in.file_attrs = 0; + io.t2open.in.open_mode = OPENX_MODE_ACCESS_RDWR | OPENX_MODE_DENY_ALL; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + +done: + cli_close(cli, fnum); + cli_unlink(cli, fname); + + return ret; +} + + +/* + test RAW_OPEN_NTCREATEX +*/ +static BOOL test_ntcreatex(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + union smb_open io; + union smb_fileinfo finfo; + const char *fname = BASEDIR "\\torture_ntcreatex.txt"; + const char *dname = BASEDIR "\\torture_ntcreatex.dir"; + NTSTATUS status; + int fnum; + BOOL ret = True; + int i; + struct { + uint32 open_disp; + BOOL with_file; + NTSTATUS correct_status; + } open_funcs[] = { + { NTCREATEX_DISP_SUPERSEDE, True, NT_STATUS_OK }, + { NTCREATEX_DISP_SUPERSEDE, False, NT_STATUS_OK }, + { NTCREATEX_DISP_OPEN, True, NT_STATUS_OK }, + { NTCREATEX_DISP_OPEN, False, NT_STATUS_OBJECT_NAME_NOT_FOUND }, + { NTCREATEX_DISP_CREATE, True, NT_STATUS_OBJECT_NAME_COLLISION }, + { NTCREATEX_DISP_CREATE, False, NT_STATUS_OK }, + { NTCREATEX_DISP_OPEN_IF, True, NT_STATUS_OK }, + { NTCREATEX_DISP_OPEN_IF, False, NT_STATUS_OK }, + { NTCREATEX_DISP_OVERWRITE, True, NT_STATUS_OK }, + { NTCREATEX_DISP_OVERWRITE, False, NT_STATUS_OBJECT_NAME_NOT_FOUND }, + { NTCREATEX_DISP_OVERWRITE_IF, True, NT_STATUS_OK }, + { NTCREATEX_DISP_OVERWRITE_IF, False, NT_STATUS_OK }, + { 6, True, NT_STATUS_INVALID_PARAMETER }, + { 6, False, NT_STATUS_INVALID_PARAMETER }, + }; + + printf("Checking RAW_OPEN_NTCREATEX\n"); + + /* reasonable default parameters */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED; + io.ntcreatex.in.root_fid = 0; + io.ntcreatex.in.access_mask = GENERIC_RIGHTS_FILE_ALL_ACCESS; + io.ntcreatex.in.alloc_size = 1024*1024; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + /* test the open disposition */ + for (i=0; itree, mem_ctx, &io); + if (!NT_STATUS_EQUAL(status, open_funcs[i].correct_status)) { + printf("(%d) incorrect status %s should be %s (i=%d with_file=%d open_disp=%d)\n", + __LINE__, nt_errstr(status), nt_errstr(open_funcs[i].correct_status), + i, (int)open_funcs[i].with_file, (int)open_funcs[i].open_disp); + ret = False; + } + if (NT_STATUS_IS_OK(status) || open_funcs[i].with_file) { + cli_close(cli, io.ntcreatex.out.fnum); + cli_unlink(cli, fname); + } + } + + /* basic field testing */ + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.fnum; + + CHECK_VAL(io.ntcreatex.out.oplock_level, 0); + CHECK_VAL(io.ntcreatex.out.create_action, NTCREATEX_ACTION_CREATED); + CHECK_NTTIME(io.ntcreatex.out.create_time, create_time); + CHECK_NTTIME(io.ntcreatex.out.access_time, access_time); + CHECK_NTTIME(io.ntcreatex.out.write_time, write_time); + CHECK_NTTIME(io.ntcreatex.out.change_time, change_time); + CHECK_ALL_INFO(io.ntcreatex.out.attrib, attrib); + CHECK_ALL_INFO(io.ntcreatex.out.alloc_size, alloc_size); + CHECK_ALL_INFO(io.ntcreatex.out.size, size); + CHECK_ALL_INFO(io.ntcreatex.out.is_directory, directory); + CHECK_VAL(io.ntcreatex.out.file_type, FILE_TYPE_DISK); + + /* check fields when the file already existed */ + cli_close(cli, fnum); + cli_unlink(cli, fname); + fnum = create_complex_file(cli, mem_ctx, fname); + if (fnum == -1) { + ret = False; + goto done; + } + cli_close(cli, fnum); + + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.fnum; + + CHECK_VAL(io.ntcreatex.out.oplock_level, 0); + CHECK_VAL(io.ntcreatex.out.create_action, NTCREATEX_ACTION_EXISTED); + CHECK_NTTIME(io.ntcreatex.out.create_time, create_time); + CHECK_NTTIME(io.ntcreatex.out.access_time, access_time); + CHECK_NTTIME(io.ntcreatex.out.write_time, write_time); + CHECK_NTTIME(io.ntcreatex.out.change_time, change_time); + CHECK_ALL_INFO(io.ntcreatex.out.attrib, attrib); + CHECK_ALL_INFO(io.ntcreatex.out.alloc_size, alloc_size); + CHECK_ALL_INFO(io.ntcreatex.out.size, size); + CHECK_ALL_INFO(io.ntcreatex.out.is_directory, directory); + CHECK_VAL(io.ntcreatex.out.file_type, FILE_TYPE_DISK); + cli_close(cli, fnum); + cli_unlink(cli, fname); + + + /* create a directory */ + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.access_mask = GENERIC_RIGHTS_FILE_ALL_ACCESS; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_DIRECTORY; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.fname = dname; + fname = dname; + + cli_rmdir(cli, fname); + cli_unlink(cli, fname); + + io.ntcreatex.in.access_mask = SEC_RIGHT_MAXIMUM_ALLOWED; + io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.fnum; + + CHECK_VAL(io.ntcreatex.out.oplock_level, 0); + CHECK_VAL(io.ntcreatex.out.create_action, NTCREATEX_ACTION_CREATED); + CHECK_NTTIME(io.ntcreatex.out.create_time, create_time); + CHECK_NTTIME(io.ntcreatex.out.access_time, access_time); + CHECK_NTTIME(io.ntcreatex.out.write_time, write_time); + CHECK_NTTIME(io.ntcreatex.out.change_time, change_time); + CHECK_ALL_INFO(io.ntcreatex.out.attrib, attrib); + CHECK_VAL(io.ntcreatex.out.attrib, FILE_ATTRIBUTE_DIRECTORY); + CHECK_ALL_INFO(io.ntcreatex.out.alloc_size, alloc_size); + CHECK_ALL_INFO(io.ntcreatex.out.size, size); + CHECK_ALL_INFO(io.ntcreatex.out.is_directory, directory); + CHECK_VAL(io.ntcreatex.out.is_directory, 1); + CHECK_VAL(io.ntcreatex.out.size, 0); + CHECK_VAL(io.ntcreatex.out.alloc_size, 0); + CHECK_VAL(io.ntcreatex.out.file_type, FILE_TYPE_DISK); + cli_unlink(cli, fname); + + +done: + cli_close(cli, fnum); + cli_unlink(cli, fname); + + return ret; +} + + +/* + test RAW_OPEN_MKNEW +*/ +static BOOL test_mknew(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + union smb_open io; + const char *fname = BASEDIR "\\torture_mknew.txt"; + NTSTATUS status; + int fnum; + BOOL ret = True; + time_t basetime = (time(NULL) + 3600*24*3) & ~1; + union smb_fileinfo finfo; + + printf("Checking RAW_OPEN_MKNEW\n"); + + io.mknew.level = RAW_OPEN_MKNEW; + io.mknew.in.attrib = 0; + io.mknew.in.write_time = 0; + io.mknew.in.fname = fname; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.mknew.out.fnum; + + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_COLLISION); + + cli_close(cli, fnum); + cli_unlink(cli, fname); + + /* make sure write_time works */ + io.mknew.in.write_time = basetime; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.mknew.out.fnum; + CHECK_TIME(basetime, write_time); + + cli_close(cli, fnum); + cli_unlink(cli, fname); + + /* make sure file_attrs works */ + io.mknew.in.attrib = FILE_ATTRIBUTE_HIDDEN; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.mknew.out.fnum; + CHECK_ALL_INFO(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_ARCHIVE, attrib); + +done: + cli_close(cli, fnum); + cli_unlink(cli, fname); + + return ret; +} + + +/* + test RAW_OPEN_CTEMP +*/ +static BOOL test_ctemp(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + union smb_open io; + NTSTATUS status; + int fnum; + BOOL ret = True; + time_t basetime = (time(NULL) + 3600*24*3) & ~1; + union smb_fileinfo finfo; + const char *name, *fname = NULL; + + printf("Checking RAW_OPEN_CTEMP\n"); + + io.ctemp.level = RAW_OPEN_CTEMP; + io.ctemp.in.attrib = FILE_ATTRIBUTE_HIDDEN; + io.ctemp.in.write_time = basetime; + io.ctemp.in.directory = BASEDIR; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ctemp.out.fnum; + + name = io.ctemp.out.name; + + finfo.generic.level = RAW_FILEINFO_NAME_INFO; + finfo.generic.in.fnum = fnum; + status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + + fname = finfo.name_info.out.fname.s; + d_printf("ctemp name=%s real name=%s\n", name, fname); + + CHECK_TIME(basetime, write_time); + +done: + cli_close(cli, fnum); + if (fname) { + cli_unlink(cli, fname); + } + + return ret; +} + +/* basic testing of all RAW_OPEN_* calls +*/ +BOOL torture_raw_open(int dummy) +{ + struct cli_state *cli; + BOOL ret = True; + TALLOC_CTX *mem_ctx; + + if (!torture_open_connection(&cli)) { + return False; + } + + mem_ctx = talloc_init("torture_raw_open"); + + if (cli_deltree(cli, BASEDIR) == -1) { + printf("Failed to clean " BASEDIR "\n"); + return False; + } + if (!cli_mkdir(cli, BASEDIR)) { + printf("Failed to create " BASEDIR " - %s\n", cli_errstr(cli)); + return False; + } + + if (!test_open(cli, mem_ctx)) { + ret = False; + } + + if (!test_openx(cli, mem_ctx)) { + ret = False; + } + + if (!test_ntcreatex(cli, mem_ctx)) { + ret = False; + } + + if (!test_t2open(cli, mem_ctx)) { + ret = False; + } + + if (!test_mknew(cli, mem_ctx)) { + ret = False; + } + + if (!test_ctemp(cli, mem_ctx)) { + ret = False; + } + + smb_raw_exit(cli->session); + cli_deltree(cli, BASEDIR); + + torture_close_connection(cli); + talloc_destroy(mem_ctx); + return ret; +} diff --git a/source4/torture/raw/oplock.c b/source4/torture/raw/oplock.c new file mode 100644 index 0000000000..888fcbdc13 --- /dev/null +++ b/source4/torture/raw/oplock.c @@ -0,0 +1,288 @@ +/* + Unix SMB/CIFS implementation. + basic raw test suite for oplocks + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 CHECK_VAL(v, correct) do { \ + if ((v) != (correct)) { \ + printf("(%d) wrong value for %s 0x%x - 0x%x\n", \ + __LINE__, #v, (int)v, (int)correct); \ + ret = False; \ + }} while (0) + +#define CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + printf("(%d) Incorrect status %s - should be %s\n", \ + __LINE__, nt_errstr(status), nt_errstr(correct)); \ + ret = False; \ + goto done; \ + }} while (0) + + +static struct { + int fnum; + unsigned char level; + int count; +} break_info; + +/* + a handler function for oplock break requests +*/ +static BOOL oplock_handler_ack(struct cli_transport *transport, uint16 tid, uint16 fnum, uint8 level, void *private) +{ + struct cli_tree *tree = private; + break_info.fnum = fnum; + break_info.level = level; + break_info.count++; + + printf("Acking in oplock handler\n"); + + return cli_oplock_ack(tree, fnum, level == 1? 0x102 : 2); +} + +/* + a handler function for oplock break requests - close the file +*/ +static BOOL oplock_handler_close(struct cli_transport *transport, uint16 tid, uint16 fnum, uint8 level, void *private) +{ + union smb_close io; + NTSTATUS status; + struct cli_tree *tree = private; + + break_info.fnum = fnum; + break_info.level = level; + break_info.count++; + + io.close.level = RAW_CLOSE_CLOSE; + io.close.in.fnum = fnum; + io.close.in.write_time = 0; + status = smb_raw_close(tree, &io); + + printf("Closing in oplock handler\n"); + + if (!NT_STATUS_IS_OK(status)) { + printf("close failed in oplock_handler_close\n"); + return False; + } + return True; +} + +/* + test oplock ops +*/ +static BOOL test_oplock(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + const char *fname = "\\test_oplock.dat"; + NTSTATUS status; + BOOL ret = True; + union smb_open io; + struct smb_unlink unl; + union smb_read rd; + uint16 fnum, fnum2; + + /* cleanup */ + cli_unlink(cli, fname); + + cli_oplock_handler(cli->transport, oplock_handler_ack, cli->tree); + + /* + base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid = 0; + io.ntcreatex.in.access_mask = GENERIC_RIGHTS_FILE_ALL_ACCESS; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + printf("open a file with a normal oplock\n"); + ZERO_STRUCT(break_info); + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | NTCREATEX_FLAGS_REQUEST_OPLOCK; + + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, EXCLUSIVE_OPLOCK_RETURN); + + printf("unlink it - should be no break\n"); + unl.in.pattern = fname; + unl.in.attrib = 0; + status = smb_raw_unlink(cli->tree, &unl); + CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION); + CHECK_VAL(break_info.count, 0); + + cli_close(cli, fnum); + + /* + with a batch oplock we get a break + */ + printf("open with batch oplock\n"); + ZERO_STRUCT(break_info); + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN); + + printf("unlink should generate a break\n"); + unl.in.pattern = fname; + unl.in.attrib = 0; + status = smb_raw_unlink(cli->tree, &unl); + CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION); + + CHECK_VAL(break_info.fnum, fnum); + CHECK_VAL(break_info.level, 2); + CHECK_VAL(break_info.count, 1); + + + cli_close(cli, fnum); + + printf("if we close on break then the unlink can succeed\n"); + ZERO_STRUCT(break_info); + cli_oplock_handler(cli->transport, oplock_handler_close, cli->tree); + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN); + + unl.in.pattern = fname; + unl.in.attrib = 0; + ZERO_STRUCT(break_info); + status = smb_raw_unlink(cli->tree, &unl); + CHECK_STATUS(status, NT_STATUS_OK); + + CHECK_VAL(break_info.fnum, fnum); + CHECK_VAL(break_info.level, 2); + CHECK_VAL(break_info.count, 1); + + printf("a self read should not cause a break\n"); + ZERO_STRUCT(break_info); + cli_close(cli, fnum); + cli_oplock_handler(cli->transport, oplock_handler_ack, cli->tree); + + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN); + + rd.read.level = RAW_READ_READ; + rd.read.in.fnum = fnum; + rd.read.in.count = 1; + rd.read.in.offset = 0; + rd.read.in.remaining = 0; + status = smb_raw_read(cli->tree, &rd); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(break_info.count, 0); + + + printf("a 2nd open should give a break\n"); + ZERO_STRUCT(break_info); + cli_close(cli, fnum); + cli_oplock_handler(cli->transport, oplock_handler_ack, cli->tree); + + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN); + + ZERO_STRUCT(break_info); + + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION); + + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.fnum, fnum); + CHECK_VAL(break_info.level, 2); + + printf("a 2nd open should get an oplock when we close instead of ack\n"); + ZERO_STRUCT(break_info); + cli_close(cli, fnum); + cli_oplock_handler(cli->transport, oplock_handler_close, cli->tree); + + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum2 = io.ntcreatex.out.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN); + + ZERO_STRUCT(break_info); + + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatex.out.fnum; + CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN); + + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.fnum, fnum2); + CHECK_VAL(break_info.level, 2); + + +done: + cli_close(cli, fnum); + cli_unlink(cli, fname); + return ret; +} + + +/* + basic testing of oplocks +*/ +BOOL torture_raw_oplock(int dummy) +{ + struct cli_state *cli1; + BOOL ret = True; + TALLOC_CTX *mem_ctx; + + if (!torture_open_connection(&cli1)) { + return False; + } + + mem_ctx = talloc_init("torture_raw_oplock"); + + if (!test_oplock(cli1, mem_ctx)) { + ret = False; + } + + torture_close_connection(cli1); + talloc_destroy(mem_ctx); + return ret; +} diff --git a/source4/torture/raw/qfileinfo.c b/source4/torture/raw/qfileinfo.c new file mode 100644 index 0000000000..b1f508cae8 --- /dev/null +++ b/source4/torture/raw/qfileinfo.c @@ -0,0 +1,701 @@ +/* + Unix SMB/CIFS implementation. + RAW_FILEINFO_* individual test suite + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 { + const char *name; + enum fileinfo_level level; + unsigned only_paths:1; + unsigned only_handles:1; + uint32 capability_mask; + NTSTATUS fnum_status, fname_status; + union smb_fileinfo fnum_finfo, fname_finfo; +} levels[] = { + { "GETATTR", RAW_FILEINFO_GETATTR, 1, 0, }, + { "GETATTRE", RAW_FILEINFO_GETATTRE, 0, 1, }, + { "STANDARD", RAW_FILEINFO_STANDARD, }, + { "EA_SIZE", RAW_FILEINFO_EA_SIZE, }, + { "ALL_EAS", RAW_FILEINFO_ALL_EAS, }, + { "IS_NAME_VALID", RAW_FILEINFO_IS_NAME_VALID, 1, 0, }, + { "BASIC_INFO", RAW_FILEINFO_BASIC_INFO, }, + { "STANDARD_INFO", RAW_FILEINFO_STANDARD_INFO, }, + { "EA_INFO", RAW_FILEINFO_EA_INFO, }, + { "NAME_INFO", RAW_FILEINFO_NAME_INFO, }, + { "ALL_INFO", RAW_FILEINFO_ALL_INFO, }, + { "ALT_NAME_INFO", RAW_FILEINFO_ALT_NAME_INFO, }, + { "STREAM_INFO", RAW_FILEINFO_STREAM_INFO, }, + { "COMPRESSION_INFO", RAW_FILEINFO_COMPRESSION_INFO, }, + { "UNIX_BASIC_INFO", RAW_FILEINFO_UNIX_BASIC, 0, 0, CAP_UNIX}, + { "UNIX_LINK_INFO", RAW_FILEINFO_UNIX_LINK, 0, 0, CAP_UNIX}, + { "BASIC_INFORMATION", RAW_FILEINFO_BASIC_INFORMATION, }, + { "STANDARD_INFORMATION", RAW_FILEINFO_STANDARD_INFORMATION, }, + { "INTERNAL_INFORMATION", RAW_FILEINFO_INTERNAL_INFORMATION, }, + { "EA_INFORMATION", RAW_FILEINFO_EA_INFORMATION, }, + { "ACCESS_INFORMATION", RAW_FILEINFO_ACCESS_INFORMATION, }, + { "NAME_INFORMATION", RAW_FILEINFO_NAME_INFORMATION, }, + { "POSITION_INFORMATION", RAW_FILEINFO_POSITION_INFORMATION, }, + { "MODE_INFORMATION", RAW_FILEINFO_MODE_INFORMATION, }, + { "ALIGNMENT_INFORMATION", RAW_FILEINFO_ALIGNMENT_INFORMATION, }, + { "ALL_INFORMATION", RAW_FILEINFO_ALL_INFORMATION, }, + { "ALT_NAME_INFORMATION", RAW_FILEINFO_ALT_NAME_INFORMATION, }, + { "STREAM_INFORMATION", RAW_FILEINFO_STREAM_INFORMATION, }, + { "COMPRESSION_INFORMATION", RAW_FILEINFO_COMPRESSION_INFORMATION, }, + { "NETWORK_OPEN_INFORMATION", RAW_FILEINFO_NETWORK_OPEN_INFORMATION, }, + { "ATTRIBUTE_TAG_INFORMATION", RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION, }, + { NULL, } +}; + +/* + compare a dos time (2 second resolution) to a nt time +*/ +static int dos_nt_time_cmp(time_t t, const NTTIME *nt) +{ + time_t t2 = nt_time_to_unix(nt); + if (ABS(t2 - t) <= 2) return 0; + return t2 - t; +} + + +/* + find a level in the levels[] table +*/ +static union smb_fileinfo *fnum_find(const char *name) +{ + int i; + for (i=0; levels[i].name; i++) { + if (NT_STATUS_IS_OK(levels[i].fnum_status) && + strcmp(name, levels[i].name) == 0 && + !levels[i].only_paths) { + return &levels[i].fnum_finfo; + } + } + return NULL; +} + +/* + find a level in the levels[] table +*/ +static union smb_fileinfo *fname_find(const char *name) +{ + int i; + for (i=0; levels[i].name; i++) { + if (NT_STATUS_IS_OK(levels[i].fname_status) && + strcmp(name, levels[i].name) == 0 && + !levels[i].only_handles) { + return &levels[i].fname_finfo; + } + } + return NULL; +} + +/* local macros to make the code below more readable */ +#define VAL_EQUAL(n1, v1, n2, v2) do {if (s1->n1.out.v1 != s2->n2.out.v2) { \ + printf("%s/%s [%u] != %s/%s [%u] at %s(%d)\n", \ + #n1, #v1, (uint_t)s1->n1.out.v1, \ + #n2, #v2, (uint_t)s2->n2.out.v2, \ + __FILE__, __LINE__); \ + ret = False; \ +}} while(0) + +#define STR_EQUAL(n1, v1, n2, v2) do {if (strcmp(s1->n1.out.v1.s, s2->n2.out.v2.s) || \ + s1->n1.out.v1.private_length != s2->n2.out.v2.private_length) { \ + printf("%s/%s [%s/%d] != %s/%s [%s/%d] at %s(%d)\n", \ + #n1, #v1, s1->n1.out.v1.s, s1->n1.out.v1.private_length, \ + #n2, #v2, s2->n2.out.v2.s, s2->n2.out.v2.private_length, \ + __FILE__, __LINE__); \ + ret = False; \ +}} while(0) + +#define STRUCT_EQUAL(n1, v1, n2, v2) do {if (memcmp(&s1->n1.out.v1,&s2->n2.out.v2,sizeof(s1->n1.out.v1))) { \ + printf("%s/%s != %s/%s at %s(%d)\n", \ + #n1, #v1, \ + #n2, #v2, \ + __FILE__, __LINE__); \ + ret = False; \ +}} while(0) + +/* used to find hints on unknown values - and to make sure + we zero-fill */ +#define VAL_UNKNOWN(n1, v1) do {if (s1->n1.out.v1 != 0) { \ + printf("%s/%s non-zero unknown - %u (0x%x) at %s(%d)\n", \ + #n1, #v1, \ + (uint_t)s1->n1.out.v1, \ + (uint_t)s1->n1.out.v1, \ + __FILE__, __LINE__); \ + ret = False; \ +}} while(0) + +/* basic testing of all RAW_FILEINFO_* calls + for each call we test that it succeeds, and where possible test + for consistency between the calls. +*/ +BOOL torture_raw_qfileinfo(int dummy) +{ + struct cli_state *cli; + int i; + BOOL ret = True; + int count; + union smb_fileinfo *s1, *s2; + TALLOC_CTX *mem_ctx; + int fnum; + const char *fname = "\\torture_qfileinfo.txt"; + NTTIME correct_time; + large_t correct_size; + uint32 correct_attrib; + const char *correct_name; + BOOL skip_streams = False; + + if (!torture_open_connection(&cli)) { + return False; + } + + mem_ctx = talloc_init("torture_qfileinfo"); + + fnum = create_complex_file(cli, mem_ctx, fname); + if (fnum == -1) { + printf("ERROR: open of %s failed (%s)\n", fname, cli_errstr(cli)); + ret = False; + goto done; + } + + + /* scan all the fileinfo and pathinfo levels */ + for (i=0; levels[i].name; i++) { + if (!levels[i].only_paths) { + levels[i].fnum_finfo.generic.level = levels[i].level; + levels[i].fnum_finfo.generic.in.fnum = fnum; + levels[i].fnum_status = smb_raw_fileinfo(cli->tree, mem_ctx, + &levels[i].fnum_finfo); + } + + if (!levels[i].only_handles) { + levels[i].fname_finfo.generic.level = levels[i].level; + levels[i].fname_finfo.generic.in.fname = talloc_strdup(mem_ctx, fname); + levels[i].fname_status = smb_raw_pathinfo(cli->tree, mem_ctx, + &levels[i].fname_finfo); + } + } + + /* check for completely broken levels */ + for (count=i=0; levels[i].name; i++) { + uint32 cap = cli->transport->negotiate.capabilities; + /* see if this server claims to support this level */ + if ((cap & levels[i].capability_mask) != levels[i].capability_mask) { + continue; + } + + if (!levels[i].only_paths && !NT_STATUS_IS_OK(levels[i].fnum_status)) { + printf("ERROR: level %s failed - %s\n", + levels[i].name, nt_errstr(levels[i].fnum_status)); + count++; + } + if (!levels[i].only_handles && !NT_STATUS_IS_OK(levels[i].fname_status)) { + printf("ERROR: level %s failed - %s\n", + levels[i].name, nt_errstr(levels[i].fname_status)); + count++; + } + } + + if (count != 0) { + ret = False; + printf("%d levels failed\n", count); + if (count > 32) { + printf("too many level failures - giving up\n"); + goto done; + } + } + + /* see if we can do streams */ + s1 = fnum_find("STREAM_INFO"); + if (!s1 || s1->stream_info.out.num_streams == 0) { + printf("STREAM_INFO broken (%d) - skipping streams checks\n", + s1 ? s1->stream_info.out.num_streams : -1); + skip_streams = True; + } + + + /* this code is incredibly repititive but doesn't lend itself to loops, so + we use lots of macros to make it less painful */ + + /* first off we check the levels that are supposed to be aliases. It will be quite rare for + this code to fail, but we need to check it for completeness */ + + + +#define ALIAS_CHECK(sname1, sname2) \ + do { \ + s1 = fnum_find(sname1); s2 = fnum_find(sname2); \ + if (s1 && s2) { INFO_CHECK } \ + s1 = fname_find(sname1); s2 = fname_find(sname2); \ + if (s1 && s2) { INFO_CHECK } \ + s1 = fnum_find(sname1); s2 = fname_find(sname2); \ + if (s1 && s2) { INFO_CHECK } \ + } while (0) + +#define INFO_CHECK \ + STRUCT_EQUAL(basic_info, create_time, basic_info, create_time); \ + STRUCT_EQUAL(basic_info, access_time, basic_info, access_time); \ + STRUCT_EQUAL(basic_info, write_time, basic_info, write_time); \ + STRUCT_EQUAL(basic_info, change_time, basic_info, change_time); \ + VAL_EQUAL (basic_info, attrib, basic_info, attrib); + + ALIAS_CHECK("BASIC_INFO", "BASIC_INFORMATION"); + +#undef INFO_CHECK +#define INFO_CHECK \ + VAL_EQUAL(standard_info, alloc_size, standard_info, alloc_size); \ + VAL_EQUAL(standard_info, size, standard_info, size); \ + VAL_EQUAL(standard_info, nlink, standard_info, nlink); \ + VAL_EQUAL(standard_info, delete_pending, standard_info, delete_pending); \ + VAL_EQUAL(standard_info, directory, standard_info, directory); + + ALIAS_CHECK("STANDARD_INFO", "STANDARD_INFORMATION"); + +#undef INFO_CHECK +#define INFO_CHECK \ + VAL_EQUAL(ea_info, ea_size, ea_info, ea_size); + + ALIAS_CHECK("EA_INFO", "EA_INFORMATION"); + +#undef INFO_CHECK +#define INFO_CHECK \ + STR_EQUAL(name_info, fname, name_info, fname); + + ALIAS_CHECK("NAME_INFO", "NAME_INFORMATION"); + +#undef INFO_CHECK +#define INFO_CHECK \ + STRUCT_EQUAL(all_info, create_time, all_info, create_time); \ + STRUCT_EQUAL(all_info, access_time, all_info, access_time); \ + STRUCT_EQUAL(all_info, write_time, all_info, write_time); \ + STRUCT_EQUAL(all_info, change_time, all_info, change_time); \ + VAL_EQUAL(all_info, attrib, all_info, attrib); \ + VAL_EQUAL(all_info, alloc_size, all_info, alloc_size); \ + VAL_EQUAL(all_info, size, all_info, size); \ + VAL_EQUAL(all_info, nlink, all_info, nlink); \ + VAL_EQUAL(all_info, delete_pending, all_info, delete_pending); \ + VAL_EQUAL(all_info, directory, all_info, directory); \ + VAL_EQUAL(all_info, ea_size, all_info, ea_size); \ + STR_EQUAL(all_info, fname, all_info, fname); + + ALIAS_CHECK("ALL_INFO", "ALL_INFORMATION"); + +#undef INFO_CHECK +#define INFO_CHECK \ + VAL_EQUAL(compression_info, compressed_size,compression_info, compressed_size); \ + VAL_EQUAL(compression_info, format, compression_info, format); \ + VAL_EQUAL(compression_info, unit_shift, compression_info, unit_shift); \ + VAL_EQUAL(compression_info, chunk_shift, compression_info, chunk_shift); \ + VAL_EQUAL(compression_info, cluster_shift, compression_info, cluster_shift); + + ALIAS_CHECK("COMPRESSION_INFO", "COMPRESSION_INFORMATION"); + + +#undef INFO_CHECK +#define INFO_CHECK \ + STR_EQUAL(alt_name_info, fname, alt_name_info, fname); + + ALIAS_CHECK("ALT_NAME_INFO", "ALT_NAME_INFORMATION"); + + +#define TIME_CHECK_NT(sname, stype, tfield) do { \ + s1 = fnum_find(sname); \ + if (s1 && memcmp(&s1->stype.out.tfield, &correct_time, sizeof(correct_time)) != 0) { \ + printf("(%d) handle %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \ + nt_time_string(mem_ctx, &s1->stype.out.tfield), \ + nt_time_string(mem_ctx, &correct_time)); \ + ret = False; \ + } \ + s1 = fname_find(sname); \ + if (s1 && memcmp(&s1->stype.out.tfield, &correct_time, sizeof(correct_time)) != 0) { \ + printf("(%d) path %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \ + nt_time_string(mem_ctx, &s1->stype.out.tfield), \ + nt_time_string(mem_ctx, &correct_time)); \ + ret = False; \ + }} while (0) + +#define TIME_CHECK_DOS(sname, stype, tfield) do { \ + s1 = fnum_find(sname); \ + if (s1 && dos_nt_time_cmp(s1->stype.out.tfield, &correct_time) != 0) { \ + printf("(%d) handle %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \ + time_string(mem_ctx, s1->stype.out.tfield), \ + nt_time_string(mem_ctx, &correct_time)); \ + ret = False; \ + } \ + s1 = fname_find(sname); \ + if (s1 && dos_nt_time_cmp(s1->stype.out.tfield, &correct_time) != 0) { \ + printf("(%d) path %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \ + time_string(mem_ctx, s1->stype.out.tfield), \ + nt_time_string(mem_ctx, &correct_time)); \ + ret = False; \ + }} while (0) + +#define TIME_CHECK_UNX(sname, stype, tfield) do { \ + s1 = fnum_find(sname); \ + if (s1 && unx_nt_time_cmp(s1->stype.out.tfield, &correct_time) != 0) { \ + printf("(%d) handle %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \ + time_string(mem_ctx, s1->stype.out.tfield), \ + nt_time_string(mem_ctx, &correct_time)); \ + ret = False; \ + } \ + s1 = fname_find(sname); \ + if (s1 && unx_nt_time_cmp(s1->stype.out.tfield, &correct_time) != 0) { \ + printf("(%d) path %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \ + time_string(mem_ctx, s1->stype.out.tfield), \ + nt_time_string(mem_ctx, &correct_time)); \ + ret = False; \ + }} while (0) + + /* now check that all the times that are supposed to be equal are correct */ + s1 = fnum_find("BASIC_INFO"); + correct_time = s1->basic_info.out.create_time; + printf("create_time: %s\n", nt_time_string(mem_ctx, &correct_time)); + + TIME_CHECK_NT ("BASIC_INFO", basic_info, create_time); + TIME_CHECK_NT ("BASIC_INFORMATION", basic_info, create_time); + TIME_CHECK_DOS("GETATTRE", getattre, create_time); + TIME_CHECK_DOS("STANDARD", standard, create_time); + TIME_CHECK_DOS("EA_SIZE", ea_size, create_time); + TIME_CHECK_NT ("ALL_INFO", all_info, create_time); + TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information, create_time); + + s1 = fnum_find("BASIC_INFO"); + correct_time = s1->basic_info.out.access_time; + printf("access_time: %s\n", nt_time_string(mem_ctx, &correct_time)); + + TIME_CHECK_NT ("BASIC_INFO", basic_info, access_time); + TIME_CHECK_NT ("BASIC_INFORMATION", basic_info, access_time); + TIME_CHECK_DOS("GETATTRE", getattre, access_time); + TIME_CHECK_DOS("STANDARD", standard, access_time); + TIME_CHECK_DOS("EA_SIZE", ea_size, access_time); + TIME_CHECK_NT ("ALL_INFO", all_info, access_time); + TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information, access_time); + + s1 = fnum_find("BASIC_INFO"); + correct_time = s1->basic_info.out.write_time; + printf("write_time : %s\n", nt_time_string(mem_ctx, &correct_time)); + + TIME_CHECK_NT ("BASIC_INFO", basic_info, write_time); + TIME_CHECK_NT ("BASIC_INFORMATION", basic_info, write_time); + TIME_CHECK_DOS("GETATTR", getattr, write_time); + TIME_CHECK_DOS("GETATTRE", getattre, write_time); + TIME_CHECK_DOS("STANDARD", standard, write_time); + TIME_CHECK_DOS("EA_SIZE", ea_size, write_time); + TIME_CHECK_NT ("ALL_INFO", all_info, write_time); + TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information, write_time); + + s1 = fnum_find("BASIC_INFO"); + correct_time = s1->basic_info.out.change_time; + printf("change_time: %s\n", nt_time_string(mem_ctx, &correct_time)); + + TIME_CHECK_NT ("BASIC_INFO", basic_info, change_time); + TIME_CHECK_NT ("BASIC_INFORMATION", basic_info, change_time); + TIME_CHECK_NT ("ALL_INFO", all_info, change_time); + TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information, change_time); + + +#define SIZE_CHECK(sname, stype, tfield) do { \ + s1 = fnum_find(sname); \ + if (s1 && s1->stype.out.tfield != correct_size) { \ + printf("(%d) handle %s/%s incorrect - %u should be %u\n", __LINE__, #stype, #tfield, \ + (unsigned)s1->stype.out.tfield, \ + (unsigned)correct_size); \ + ret = False; \ + } \ + s1 = fname_find(sname); \ + if (s1 && s1->stype.out.tfield != correct_size) { \ + printf("(%d) path %s/%s incorrect - %u should be %u\n", __LINE__, #stype, #tfield, \ + (unsigned)s1->stype.out.tfield, \ + (unsigned)correct_size); \ + ret = False; \ + }} while (0) + + s1 = fnum_find("STANDARD_INFO"); + correct_size = s1->standard_info.out.size; + printf("size: %u\n", (unsigned)correct_size); + + SIZE_CHECK("GETATTR", getattr, size); + SIZE_CHECK("GETATTRE", getattre, size); + SIZE_CHECK("STANDARD", standard, size); + SIZE_CHECK("EA_SIZE", ea_size, size); + SIZE_CHECK("STANDARD_INFO", standard_info, size); + SIZE_CHECK("STANDARD_INFORMATION", standard_info, size); + SIZE_CHECK("ALL_INFO", all_info, size); + SIZE_CHECK("ALL_INFORMATION", all_info, size); + SIZE_CHECK("COMPRESSION_INFO", compression_info, compressed_size); + SIZE_CHECK("COMPRESSION_INFORMATION", compression_info, compressed_size); + SIZE_CHECK("NETWORK_OPEN_INFORMATION", network_open_information, size); + if (!skip_streams) { + SIZE_CHECK("STREAM_INFO", stream_info, streams[0].size); + SIZE_CHECK("STREAM_INFORMATION", stream_info, streams[0].size); + } + + + s1 = fnum_find("STANDARD_INFO"); + correct_size = s1->standard_info.out.alloc_size; + printf("alloc_size: %u\n", (unsigned)correct_size); + + SIZE_CHECK("GETATTRE", getattre, alloc_size); + SIZE_CHECK("STANDARD", standard, alloc_size); + SIZE_CHECK("EA_SIZE", ea_size, alloc_size); + SIZE_CHECK("STANDARD_INFO", standard_info, alloc_size); + SIZE_CHECK("STANDARD_INFORMATION", standard_info, alloc_size); + SIZE_CHECK("ALL_INFO", all_info, alloc_size); + SIZE_CHECK("ALL_INFORMATION", all_info, alloc_size); + SIZE_CHECK("NETWORK_OPEN_INFORMATION", network_open_information, alloc_size); + if (!skip_streams) { + SIZE_CHECK("STREAM_INFO", stream_info, streams[0].alloc_size); + SIZE_CHECK("STREAM_INFORMATION", stream_info, streams[0].alloc_size); + } + +#define ATTRIB_CHECK(sname, stype, tfield) do { \ + s1 = fnum_find(sname); \ + if (s1 && s1->stype.out.tfield != correct_attrib) { \ + printf("(%d) handle %s/%s incorrect - 0x%x should be 0x%x\n", __LINE__, #stype, #tfield, \ + (unsigned)s1->stype.out.tfield, \ + (unsigned)correct_attrib); \ + ret = False; \ + } \ + s1 = fname_find(sname); \ + if (s1 && s1->stype.out.tfield != correct_attrib) { \ + printf("(%d) path %s/%s incorrect - 0x%x should be 0x%x\n", __LINE__, #stype, #tfield, \ + (unsigned)s1->stype.out.tfield, \ + (unsigned)correct_attrib); \ + ret = False; \ + }} while (0) + + s1 = fnum_find("BASIC_INFO"); + correct_attrib = s1->basic_info.out.attrib; + printf("attrib: 0x%x\n", (unsigned)correct_attrib); + + ATTRIB_CHECK("GETATTR", getattr, attrib); + ATTRIB_CHECK("GETATTRE", getattre, attrib); + ATTRIB_CHECK("STANDARD", standard, attrib); + ATTRIB_CHECK("BASIC_INFO", basic_info, attrib); + ATTRIB_CHECK("BASIC_INFORMATION", basic_info, attrib); + ATTRIB_CHECK("EA_SIZE", ea_size, attrib); + ATTRIB_CHECK("ALL_INFO", all_info, attrib); + ATTRIB_CHECK("ALL_INFORMATION", all_info, attrib); + ATTRIB_CHECK("NETWORK_OPEN_INFORMATION", network_open_information, attrib); + ATTRIB_CHECK("ATTRIBUTE_TAG_INFORMATION", attribute_tag_information, attrib); + + correct_name = fname; + printf("name: %s\n", correct_name); + +#define NAME_CHECK(sname, stype, tfield, flags) do { \ + s1 = fnum_find(sname); \ + if ((s1 && strcmp(s1->stype.out.tfield.s, correct_name) != 0) || \ + wire_bad_flags(&s1->stype.out.tfield, flags)) { \ + printf("(%d) handle %s/%s incorrect - '%s/%d'\n", __LINE__, #stype, #tfield, \ + s1->stype.out.tfield.s, s1->stype.out.tfield.private_length); \ + ret = False; \ + } \ + s1 = fname_find(sname); \ + if ((s1 && strcmp(s1->stype.out.tfield.s, correct_name)) != 0 || \ + wire_bad_flags(&s1->stype.out.tfield, flags)) { \ + printf("(%d) path %s/%s incorrect - '%s/%d'\n", __LINE__, #stype, #tfield, \ + s1->stype.out.tfield.s, s1->stype.out.tfield.private_length); \ + ret = False; \ + }} while (0) + + NAME_CHECK("NAME_INFO", name_info, fname, STR_UNICODE); + NAME_CHECK("NAME_INFORMATION", name_info, fname, STR_UNICODE); + + /* the ALL_INFO file name is the full path on the filesystem */ + s1 = fnum_find("ALL_INFO"); + if (s1 && !s1->all_info.out.fname.s) { + printf("ALL_INFO didn't give a filename\n"); + ret = False; + } + if (s1 && s1->all_info.out.fname.s) { + char *p = strrchr(s1->all_info.out.fname.s, '\\'); + if (!p) { + printf("Not a full path in all_info/fname? - '%s'\n", + s1->all_info.out.fname.s); + ret = False; + } else { + if (strcmp(correct_name, p) != 0) { + printf("incorrect basename in all_info/fname - '%s'\n", + s1->all_info.out.fname.s); + ret = False; + } + } + if (wire_bad_flags(&s1->all_info.out.fname, STR_UNICODE)) { + printf("Should not null terminate all_info/fname\n"); + ret = False; + } + } + + s1 = fnum_find("ALT_NAME_INFO"); + correct_name = s1->alt_name_info.out.fname.s; + printf("alt_name: %s\n", correct_name); + + NAME_CHECK("ALT_NAME_INFO", alt_name_info, fname, STR_UNICODE); + NAME_CHECK("ALT_NAME_INFORMATION", alt_name_info, fname, STR_UNICODE); + + /* and make sure we can open by alternate name */ + cli_close(cli, fnum); + fnum = cli_nt_create_full(cli, correct_name, 0, GENERIC_RIGHTS_FILE_ALL_ACCESS, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_DELETE| + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE, + NTCREATEX_DISP_OVERWRITE_IF, + 0, 0); + if (fnum == -1) { + printf("Unable to open by alt_name - %s\n", cli_errstr(cli)); + ret = False; + } + + if (!skip_streams) { + correct_name = "::$DATA"; + printf("stream_name: %s\n", correct_name); + + NAME_CHECK("STREAM_INFO", stream_info, streams[0].stream_name, STR_UNICODE); + NAME_CHECK("STREAM_INFORMATION", stream_info, streams[0].stream_name, STR_UNICODE); + } + + /* make sure the EAs look right */ + s1 = fnum_find("ALL_EAS"); + s2 = fnum_find("ALL_INFO"); + if (s1) { + for (i=0;iall_eas.out.num_eas;i++) { + printf(" flags=%d %s=%*.*s\n", + s1->all_eas.out.eas[i].flags, + s1->all_eas.out.eas[i].name.s, + s1->all_eas.out.eas[i].value.length, + s1->all_eas.out.eas[i].value.length, + s1->all_eas.out.eas[i].value.data); + } + } + if (s1 && s2) { + if (s1->all_eas.out.num_eas == 0) { + if (s2->all_info.out.ea_size != 0) { + printf("ERROR: num_eas==0 but fnum all_info.out.ea_size == %d\n", + s2->all_info.out.ea_size); + } + } else { + if (s2->all_info.out.ea_size != + ea_list_size(s1->all_eas.out.num_eas, s1->all_eas.out.eas)) { + printf("ERROR: ea_list_size=%d != fnum all_info.out.ea_size=%d\n", + ea_list_size(s1->all_eas.out.num_eas, s1->all_eas.out.eas), + s2->all_info.out.ea_size); + } + } + } + s2 = fname_find("ALL_EAS"); + if (s2) { + VAL_EQUAL(all_eas, num_eas, all_eas, num_eas); + for (i=0;iall_eas.out.num_eas;i++) { + VAL_EQUAL(all_eas, eas[i].flags, all_eas, eas[i].flags); + STR_EQUAL(all_eas, eas[i].name, all_eas, eas[i].name); + VAL_EQUAL(all_eas, eas[i].value.length, all_eas, eas[i].value.length); + } + } + +#define VAL_CHECK(sname1, stype1, tfield1, sname2, stype2, tfield2) do { \ + s1 = fnum_find(sname1); s2 = fnum_find(sname2); \ + if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \ + printf("(%d) handle %s/%s != %s/%s - 0x%x vs 0x%x\n", __LINE__, \ + #stype1, #tfield1, #stype2, #tfield2, \ + s1->stype1.out.tfield1, s2->stype2.out.tfield2); \ + ret = False; \ + } \ + s1 = fname_find(sname1); s2 = fname_find(sname2); \ + if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \ + printf("(%d) path %s/%s != %s/%s - 0x%x vs 0x%x\n", __LINE__, \ + #stype1, #tfield1, #stype2, #tfield2, \ + s1->stype1.out.tfield1, s2->stype2.out.tfield2); \ + ret = False; \ + } \ + s1 = fnum_find(sname1); s2 = fname_find(sname2); \ + if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \ + printf("(%d) handle %s/%s != path %s/%s - 0x%x vs 0x%x\n", __LINE__, \ + #stype1, #tfield1, #stype2, #tfield2, \ + s1->stype1.out.tfield1, s2->stype2.out.tfield2); \ + ret = False; \ + } \ + s1 = fname_find(sname1); s2 = fnum_find(sname2); \ + if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \ + printf("(%d) path %s/%s != handle %s/%s - 0x%x vs 0x%x\n", __LINE__, \ + #stype1, #tfield1, #stype2, #tfield2, \ + s1->stype1.out.tfield1, s2->stype2.out.tfield2); \ + ret = False; \ + }} while (0) + + VAL_CHECK("STANDARD_INFO", standard_info, delete_pending, + "ALL_INFO", all_info, delete_pending); + VAL_CHECK("STANDARD_INFO", standard_info, directory, + "ALL_INFO", all_info, directory); + VAL_CHECK("STANDARD_INFO", standard_info, nlink, + "ALL_INFO", all_info, nlink); + VAL_CHECK("EA_INFO", ea_info, ea_size, + "ALL_INFO", all_info, ea_size); + VAL_CHECK("EA_SIZE", ea_size, ea_size, + "ALL_INFO", all_info, ea_size); + + +#define NAME_PATH_CHECK(sname, stype, field) do { \ + s1 = fname_find(sname); s2 = fnum_find(sname); \ + VAL_EQUAL(stype, field, stype, field); \ +} while (0) + + NAME_PATH_CHECK("INTERNAL_INFORMATION", internal_information, device); + NAME_PATH_CHECK("INTERNAL_INFORMATION", internal_information, inode); + NAME_PATH_CHECK("POSITION_INFORMATION", position_information, position); + NAME_PATH_CHECK("MODE_INFORMATION", mode_information, mode); + NAME_PATH_CHECK("ALIGNMENT_INFORMATION", alignment_information, alignment_requirement); + NAME_PATH_CHECK("ATTRIBUTE_TAG_INFORMATION", attribute_tag_information, attrib); + NAME_PATH_CHECK("ATTRIBUTE_TAG_INFORMATION", attribute_tag_information, reparse_tag); + +#if 0 + /* these are expected to differ */ + NAME_PATH_CHECK("ACCESS_INFORMATION", access_information, access_flags); +#endif + +#define UNKNOWN_CHECK(sname, stype, tfield) do { \ + s1 = fnum_find(sname); \ + if (s1 && s1->stype.out.tfield != 0) { \ + printf("(%d) handle %s/%s unknown != 0 (0x%x)\n", __LINE__, \ + #stype, #tfield, \ + (unsigned)s1->stype.out.tfield); \ + } \ + s1 = fname_find(sname); \ + if (s1 && s1->stype.out.tfield != 0) { \ + printf("(%d) path %s/%s unknown != 0 (0x%x)\n", __LINE__, \ + #stype, #tfield, \ + (unsigned)s1->stype.out.tfield); \ + }} while (0) + + /* now get a bit fancier .... */ + + /* when we set the delete disposition then the link count should drop + to 0 and delete_pending should be 1 */ + + +done: + cli_close(cli, fnum); + cli_unlink(cli, fname); + + torture_close_connection(cli); + talloc_destroy(mem_ctx); + return ret; +} diff --git a/source4/torture/raw/qfsinfo.c b/source4/torture/raw/qfsinfo.c new file mode 100644 index 0000000000..274d1073af --- /dev/null +++ b/source4/torture/raw/qfsinfo.c @@ -0,0 +1,295 @@ +/* + Unix SMB/CIFS implementation. + RAW_QFS_* individual test suite + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 { + const char *name; + enum fsinfo_level level; + uint32 capability_mask; + NTSTATUS status; + union smb_fsinfo fsinfo; +} levels[] = { + {"DSKATTR", RAW_QFS_DSKATTR, }, + {"ALLOCATION", RAW_QFS_ALLOCATION, }, + {"VOLUME", RAW_QFS_VOLUME, }, + {"VOLUME_INFO", RAW_QFS_VOLUME_INFO, }, + {"SIZE_INFO", RAW_QFS_SIZE_INFO, }, + {"DEVICE_INFO", RAW_QFS_DEVICE_INFO, }, + {"ATTRIBUTE_INFO", RAW_QFS_ATTRIBUTE_INFO, }, + {"UNIX_INFO", RAW_QFS_UNIX_INFO, CAP_UNIX}, + {"VOLUME_INFORMATION", RAW_QFS_VOLUME_INFORMATION, }, + {"SIZE_INFORMATION", RAW_QFS_SIZE_INFORMATION, }, + {"DEVICE_INFORMATION", RAW_QFS_DEVICE_INFORMATION, }, + {"ATTRIBUTE_INFORMATION", RAW_QFS_ATTRIBUTE_INFORMATION, }, + {"QUOTA_INFORMATION", RAW_QFS_QUOTA_INFORMATION, }, + {"FULL_SIZE_INFORMATION", RAW_QFS_FULL_SIZE_INFORMATION, }, + {"OBJECTID_INFORMATION", RAW_QFS_OBJECTID_INFORMATION, }, + { NULL, } +}; + + +/* + find a level in the levels[] table +*/ +static union smb_fsinfo *find(const char *name) +{ + int i; + for (i=0; levels[i].name; i++) { + if (strcmp(name, levels[i].name) == 0) { + return &levels[i].fsinfo; + } + } + return NULL; +} + +/* local macros to make the code below more readable */ +#define VAL_EQUAL(n1, v1, n2, v2) do {if (s1->n1.out.v1 != s2->n2.out.v2) { \ + printf("%s/%s [%u] != %s/%s [%u] at %s(%d)\n", \ + #n1, #v1, (uint_t)s1->n1.out.v1, \ + #n2, #v2, (uint_t)s2->n2.out.v2, \ + __FILE__, __LINE__); \ + ret = False; \ +}} while(0) + +#define STR_EQUAL(n1, v1, n2, v2) do {if (!s1->n1.out.v1 && !s2->n2.out.v2) return True; \ + if (!s1->n1.out.v1 || !s2->n2.out.v2) return False; \ + if (strcmp(s1->n1.out.v1, s2->n2.out.v2)) { \ + printf("%s/%s [%s] != %s/%s [%s] at %s(%d)\n", \ + #n1, #v1, s1->n1.out.v1, \ + #n2, #v2, s2->n2.out.v2, \ + __FILE__, __LINE__); \ + ret = False; \ +}} while(0) + +#define STRUCT_EQUAL(n1, v1, n2, v2) do {if (memcmp(&s1->n1.out.v1,&s2->n2.out.v2,sizeof(s1->n1.out.v1))) { \ + printf("%s/%s != %s/%s at %s(%d)\n", \ + #n1, #v1, \ + #n2, #v2, \ + __FILE__, __LINE__); \ + ret = False; \ +}} while(0) + +/* used to find hints on unknown values - and to make sure + we zero-fill */ +#define VAL_UNKNOWN(n1, v1) do {if (s1->n1.out.v1 != 0) { \ + printf("%s/%s non-zero unknown - %u (0x%x) at %s(%d)\n", \ + #n1, #v1, \ + (uint_t)s1->n1.out.v1, \ + (uint_t)s1->n1.out.v1, \ + __FILE__, __LINE__); \ + ret = False; \ +}} while(0) + +/* basic testing of all RAW_QFS_* calls + for each call we test that it succeeds, and where possible test + for consistency between the calls. + + Some of the consistency tests assume that the target filesystem is + quiescent, which is sometimes hard to achieve +*/ +BOOL torture_raw_qfsinfo(int dummy) +{ + struct cli_state *cli; + int i; + BOOL ret = True; + int count; + union smb_fsinfo *s1, *s2; + TALLOC_CTX *mem_ctx; + + if (!torture_open_connection(&cli)) { + return False; + } + + mem_ctx = talloc_init("torture_qfsinfo"); + + /* scan all the levels, pulling the results */ + for (i=0; levels[i].name; i++) { + printf("Running level %s\n", levels[i].name); + levels[i].fsinfo.generic.level = levels[i].level; + levels[i].status = smb_raw_fsinfo(cli->tree, mem_ctx, &levels[i].fsinfo); + } + + /* check for completely broken levels */ + for (count=i=0; levels[i].name; i++) { + uint32 cap = cli->transport->negotiate.capabilities; + /* see if this server claims to support this level */ + if ((cap & levels[i].capability_mask) != levels[i].capability_mask) { + continue; + } + + if (!NT_STATUS_IS_OK(levels[i].status)) { + printf("ERROR: level %s failed - %s\n", + levels[i].name, nt_errstr(levels[i].status)); + count++; + } + } + + if (count != 0) { + ret = False; + printf("%d levels failed\n", count); + if (count > 10) { + printf("too many level failures - giving up\n"); + goto done; + } + } + + printf("check for correct aliases\n"); + s1 = find("SIZE_INFO"); + s2 = find("SIZE_INFORMATION"); + if (s1 && s2) { + VAL_EQUAL(size_info, total_alloc_units, size_info, total_alloc_units); + VAL_EQUAL(size_info, avail_alloc_units, size_info, avail_alloc_units); + VAL_EQUAL(size_info, sectors_per_unit, size_info, sectors_per_unit); + VAL_EQUAL(size_info, bytes_per_sector, size_info, bytes_per_sector); + } + + s1 = find("DEVICE_INFO"); + s2 = find("DEVICE_INFORMATION"); + if (s1 && s2) { + VAL_EQUAL(device_info, device_type, device_info, device_type); + VAL_EQUAL(device_info, characteristics, device_info, characteristics); + } + + s1 = find("VOLUME_INFO"); + s2 = find("VOLUME_INFORMATION"); + if (s1 && s2) { + STRUCT_EQUAL(volume_info, create_time, volume_info, create_time); + VAL_EQUAL (volume_info, serial_number, volume_info, serial_number); + STR_EQUAL (volume_info, volume_name.s, volume_info, volume_name.s); + printf("volume_info.volume_name = '%s'\n", s1->volume_info.out.volume_name.s); + } + + s1 = find("ATTRIBUTE_INFO"); + s2 = find("ATTRIBUTE_INFORMATION"); + if (s1 && s2) { + VAL_EQUAL(attribute_info, fs_attr, + attribute_info, fs_attr); + VAL_EQUAL(attribute_info, max_file_component_length, + attribute_info, max_file_component_length); + STR_EQUAL(attribute_info, fs_type.s, attribute_info, fs_type.s); + printf("attribute_info.fs_type = '%s'\n", s1->attribute_info.out.fs_type.s); + } + + printf("check for consistent disk sizes\n"); + s1 = find("DSKATTR"); + s2 = find("ALLOCATION"); + if (s1 && s2) { + double size1, size2; + double scale = s1->dskattr.out.blocks_per_unit * s1->dskattr.out.block_size; + size1 = 1.0 * + s1->dskattr.out.units_total * + s1->dskattr.out.blocks_per_unit * + s1->dskattr.out.block_size / scale; + size2 = 1.0 * + s2->allocation.out.sectors_per_unit * + s2->allocation.out.total_alloc_units * + s2->allocation.out.bytes_per_sector / scale; + if (ABS(size1 - size2) > 1) { + printf("Inconsistent total size in DSKATTR and ALLOCATION - size1=%.0f size2=%.0f\n", + size1, size2); + ret = False; + } + printf("total disk = %.0f MB\n", size1*scale/1.0e6); + } + + printf("check consistent free disk space\n"); + s1 = find("DSKATTR"); + s2 = find("ALLOCATION"); + if (s1 && s2) { + double size1, size2; + double scale = s1->dskattr.out.blocks_per_unit * s1->dskattr.out.block_size; + size1 = 1.0 * + s1->dskattr.out.units_free * + s1->dskattr.out.blocks_per_unit * + s1->dskattr.out.block_size / scale; + size2 = 1.0 * + s2->allocation.out.sectors_per_unit * + s2->allocation.out.avail_alloc_units * + s2->allocation.out.bytes_per_sector / scale; + if (ABS(size1 - size2) > 1) { + printf("Inconsistent avail size in DSKATTR and ALLOCATION - size1=%.0f size2=%.0f\n", + size1, size2); + ret = False; + } + printf("free disk = %.0f MB\n", size1*scale/1.0e6); + } + + printf("volume info consistency\n"); + s1 = find("VOLUME"); + s2 = find("VOLUME_INFO"); + if (s1 && s2) { + VAL_EQUAL(volume, serial_number, volume_info, serial_number); + STR_EQUAL(volume, volume_name.s, volume_info, volume_name.s); + } + + /* disk size consistency - notice that 'avail_alloc_units' maps to the caller + available allocation units, not the total */ + s1 = find("SIZE_INFO"); + s2 = find("FULL_SIZE_INFORMATION"); + if (s1 && s2) { + VAL_EQUAL(size_info, total_alloc_units, full_size_information, total_alloc_units); + VAL_EQUAL(size_info, avail_alloc_units, full_size_information, call_avail_alloc_units); + VAL_EQUAL(size_info, sectors_per_unit, full_size_information, sectors_per_unit); + VAL_EQUAL(size_info, bytes_per_sector, full_size_information, bytes_per_sector); + } + + printf("check for non-zero unknown fields\n"); + s1 = find("QUOTA_INFORMATION"); + if (s1) { + VAL_UNKNOWN(quota_information, unknown[0]); + VAL_UNKNOWN(quota_information, unknown[1]); + VAL_UNKNOWN(quota_information, unknown[2]); + } + + s1 = find("OBJECTID_INFORMATION"); + if (s1) { + VAL_UNKNOWN(objectid_information, unknown[0]); + VAL_UNKNOWN(objectid_information, unknown[1]); + VAL_UNKNOWN(objectid_information, unknown[2]); + VAL_UNKNOWN(objectid_information, unknown[3]); + VAL_UNKNOWN(objectid_information, unknown[4]); + VAL_UNKNOWN(objectid_information, unknown[5]); + } + + +#define STR_CHECK(sname, stype, field, flags) do { \ + s1 = find(sname); \ + if (s1) { \ + if (wire_bad_flags(&s1->stype.out.field, flags)) { \ + printf("(%d) incorrect string termination in %s/%s\n", \ + __LINE__, #stype, #field); \ + ret = False; \ + } \ + }} while (0) + + printf("check for correct termination\n"); + STR_CHECK("VOLUME", volume, volume_name, 0); + STR_CHECK("VOLUME_INFO", volume_info, volume_name, STR_UNICODE); + STR_CHECK("VOLUME_INFORMATION", volume_info, volume_name, STR_UNICODE); + STR_CHECK("ATTRIBUTE_INFO", attribute_info, fs_type, STR_UNICODE); + STR_CHECK("ATTRIBUTE_INFORMATION", attribute_info, fs_type, STR_UNICODE); + +done: + torture_close_connection(cli); + talloc_destroy(mem_ctx); + return ret; +} diff --git a/source4/torture/raw/read.c b/source4/torture/raw/read.c new file mode 100644 index 0000000000..c231f52c9d --- /dev/null +++ b/source4/torture/raw/read.c @@ -0,0 +1,732 @@ +/* + Unix SMB/CIFS implementation. + test suite for various read operations + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + printf("(%d) Incorrect status %s - should be %s\n", \ + __LINE__, nt_errstr(status), nt_errstr(correct)); \ + ret = False; \ + goto done; \ + }} while (0) + +#define CHECK_VALUE(v, correct) do { \ + if ((v) != (correct)) { \ + printf("(%d) Incorrect value %s=%d - should be %d\n", \ + __LINE__, #v, v, correct); \ + ret = False; \ + goto done; \ + }} while (0) + +#define CHECK_BUFFER(buf, seed, len) do { \ + if (!check_buffer(buf, seed, len, __LINE__)) { \ + ret = False; \ + goto done; \ + }} while (0) + +#define BASEDIR "\\testread" + + +/* + setup a random buffer based on a seed +*/ +static void setup_buffer(char *buf, unsigned seed, int len) +{ + int i; + srandom(seed); + for (i=0;itree, &io); + + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.read.out.nread, 0); + + printf("Trying zero file read\n"); + io.read.in.count = 0; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.read.out.nread, 0); + + printf("Trying bad fnum\n"); + io.read.in.fnum = fnum+1; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + io.read.in.fnum = fnum; + + cli_write(cli, fnum, 0, test_data, 0, strlen(test_data)); + + printf("Trying small read\n"); + io.read.in.fnum = fnum; + io.read.in.offset = 0; + io.read.in.remaining = 0; + io.read.in.count = strlen(test_data); + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.read.out.nread, strlen(test_data)); + if (memcmp(buf, test_data, strlen(test_data)) != 0) { + ret = False; + printf("incorrect data at %d!? (%s:%s)\n", __LINE__, test_data, buf); + goto done; + } + + printf("Trying short read\n"); + io.read.in.offset = 1; + io.read.in.count = strlen(test_data); + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.read.out.nread, strlen(test_data)-1); + if (memcmp(buf, test_data+1, strlen(test_data)-1) != 0) { + ret = False; + printf("incorrect data at %d!? (%s:%s)\n", __LINE__, test_data+1, buf); + goto done; + } + + printf("Trying max offset\n"); + io.read.in.offset = ~0; + io.read.in.count = strlen(test_data); + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.read.out.nread, 0); + + setup_buffer(buf, seed, maxsize); + cli_write(cli, fnum, 0, buf, 0, maxsize); + memset(buf, 0, maxsize); + + printf("Trying large read\n"); + io.read.in.offset = 0; + io.read.in.count = ~0; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_BUFFER(buf, seed, io.read.out.nread); + + + printf("Trying locked region\n"); + cli->session->pid++; + if (!cli_lock(cli, fnum, 103, 1, 0, WRITE_LOCK)) { + printf("Failed to lock file at %d\n", __LINE__); + ret = False; + goto done; + } + cli->session->pid--; + memset(buf, 0, maxsize); + io.read.in.offset = 0; + io.read.in.count = ~0; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + +done: + smb_raw_exit(cli->session); + cli_deltree(cli, BASEDIR); + return ret; +} + + +/* + test lockread ops +*/ +static BOOL test_lockread(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + union smb_read io; + NTSTATUS status; + BOOL ret = True; + int fnum; + char *buf; + const int maxsize = 90000; + const char *fname = BASEDIR "\\test.txt"; + const char *test_data = "TEST DATA"; + unsigned seed = time(NULL); + + buf = talloc_zero(mem_ctx, maxsize); + + if (cli_deltree(cli, BASEDIR) == -1 || + !cli_mkdir(cli, BASEDIR)) { + printf("Unable to setup %s - %s\n", BASEDIR, cli_errstr(cli)); + return False; + } + + printf("Testing RAW_READ_LOCKREAD\n"); + io.generic.level = RAW_READ_LOCKREAD; + + fnum = cli_open(cli, fname, O_RDWR|O_CREAT, DENY_NONE); + if (fnum == -1) { + printf("Failed to create %s - %s\n", fname, cli_errstr(cli)); + ret = False; + goto done; + } + + printf("Trying empty file read\n"); + io.lockread.in.fnum = fnum; + io.lockread.in.count = 1; + io.lockread.in.offset = 0; + io.lockread.in.remaining = 0; + io.lockread.out.data = buf; + status = smb_raw_read(cli->tree, &io); + + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.lockread.out.nread, 0); + + printf("Trying zero file read\n"); + io.lockread.in.count = 0; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + printf("Trying bad fnum\n"); + io.lockread.in.fnum = fnum+1; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + io.lockread.in.fnum = fnum; + + cli_write(cli, fnum, 0, test_data, 0, strlen(test_data)); + + printf("Trying small read\n"); + io.lockread.in.fnum = fnum; + io.lockread.in.offset = 0; + io.lockread.in.remaining = 0; + io.lockread.in.count = strlen(test_data); + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + cli_unlock(cli, fnum, 0, 1); + + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.lockread.out.nread, strlen(test_data)); + if (memcmp(buf, test_data, strlen(test_data)) != 0) { + ret = False; + printf("incorrect data at %d!? (%s:%s)\n", __LINE__, test_data, buf); + goto done; + } + + printf("Trying short read\n"); + io.lockread.in.offset = 1; + io.lockread.in.count = strlen(test_data); + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + cli_unlock(cli, fnum, 0, strlen(test_data)); + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + CHECK_VALUE(io.lockread.out.nread, strlen(test_data)-1); + if (memcmp(buf, test_data+1, strlen(test_data)-1) != 0) { + ret = False; + printf("incorrect data at %d!? (%s:%s)\n", __LINE__, test_data+1, buf); + goto done; + } + + printf("Trying max offset\n"); + io.lockread.in.offset = ~0; + io.lockread.in.count = strlen(test_data); + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.lockread.out.nread, 0); + + setup_buffer(buf, seed, maxsize); + cli_write(cli, fnum, 0, buf, 0, maxsize); + memset(buf, 0, maxsize); + + printf("Trying large read\n"); + io.lockread.in.offset = 0; + io.lockread.in.count = ~0; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + cli_unlock(cli, fnum, 1, strlen(test_data)); + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_BUFFER(buf, seed, io.lockread.out.nread); + cli_unlock(cli, fnum, 0, 0xFFFF); + + + printf("Trying locked region\n"); + cli->session->pid++; + if (!cli_lock(cli, fnum, 103, 1, 0, WRITE_LOCK)) { + printf("Failed to lock file at %d\n", __LINE__); + ret = False; + goto done; + } + cli->session->pid--; + memset(buf, 0, maxsize); + io.lockread.in.offset = 0; + io.lockread.in.count = ~0; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + +done: + cli_close(cli, fnum); + cli_deltree(cli, BASEDIR); + return ret; +} + + +/* + test readx ops +*/ +static BOOL test_readx(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + union smb_read io; + NTSTATUS status; + BOOL ret = True; + int fnum; + char *buf; + const int maxsize = 90000; + const char *fname = BASEDIR "\\test.txt"; + const char *test_data = "TEST DATA"; + unsigned seed = time(NULL); + + buf = talloc_zero(mem_ctx, maxsize); + + if (cli_deltree(cli, BASEDIR) == -1 || + !cli_mkdir(cli, BASEDIR)) { + printf("Unable to setup %s - %s\n", BASEDIR, cli_errstr(cli)); + return False; + } + + printf("Testing RAW_READ_READX\n"); + + fnum = cli_open(cli, fname, O_RDWR|O_CREAT, DENY_NONE); + if (fnum == -1) { + printf("Failed to create %s - %s\n", fname, cli_errstr(cli)); + ret = False; + goto done; + } + + printf("Trying empty file read\n"); + io.generic.level = RAW_READ_READX; + io.readx.in.fnum = fnum; + io.readx.in.mincnt = 1; + io.readx.in.maxcnt = 1; + io.readx.in.offset = 0; + io.readx.in.remaining = 0; + io.readx.out.data = buf; + status = smb_raw_read(cli->tree, &io); + + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readx.out.nread, 0); + CHECK_VALUE(io.readx.out.remaining, 0xFFFF); + CHECK_VALUE(io.readx.out.compaction_mode, 0); + + printf("Trying zero file read\n"); + io.readx.in.mincnt = 0; + io.readx.in.maxcnt = 0; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readx.out.nread, 0); + CHECK_VALUE(io.readx.out.remaining, 0xFFFF); + CHECK_VALUE(io.readx.out.compaction_mode, 0); + + printf("Trying bad fnum\n"); + io.readx.in.fnum = fnum+1; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + io.readx.in.fnum = fnum; + + cli_write(cli, fnum, 0, test_data, 0, strlen(test_data)); + + printf("Trying small read\n"); + io.readx.in.fnum = fnum; + io.readx.in.offset = 0; + io.readx.in.remaining = 0; + io.readx.in.mincnt = strlen(test_data); + io.readx.in.maxcnt = strlen(test_data); + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readx.out.nread, strlen(test_data)); + CHECK_VALUE(io.readx.out.remaining, 0xFFFF); + CHECK_VALUE(io.readx.out.compaction_mode, 0); + if (memcmp(buf, test_data, strlen(test_data)) != 0) { + ret = False; + printf("incorrect data at %d!? (%s:%s)\n", __LINE__, test_data, buf); + goto done; + } + + printf("Trying short read\n"); + io.readx.in.offset = 1; + io.readx.in.mincnt = strlen(test_data); + io.readx.in.maxcnt = strlen(test_data); + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readx.out.nread, strlen(test_data)-1); + CHECK_VALUE(io.readx.out.remaining, 0xFFFF); + CHECK_VALUE(io.readx.out.compaction_mode, 0); + if (memcmp(buf, test_data+1, strlen(test_data)-1) != 0) { + ret = False; + printf("incorrect data at %d!? (%s:%s)\n", __LINE__, test_data+1, buf); + goto done; + } + + printf("Trying max offset\n"); + io.readx.in.offset = 0xffffffff; + io.readx.in.mincnt = strlen(test_data); + io.readx.in.maxcnt = strlen(test_data); + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readx.out.nread, 0); + CHECK_VALUE(io.readx.out.remaining, 0xFFFF); + CHECK_VALUE(io.readx.out.compaction_mode, 0); + + setup_buffer(buf, seed, maxsize); + cli_write(cli, fnum, 0, buf, 0, maxsize); + memset(buf, 0, maxsize); + + printf("Trying large read\n"); + io.readx.in.offset = 0; + io.readx.in.mincnt = ~0; + io.readx.in.maxcnt = ~0; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readx.out.remaining, 0xFFFF); + CHECK_VALUE(io.readx.out.compaction_mode, 0); + CHECK_VALUE(io.readx.out.nread, io.readx.in.maxcnt); + CHECK_BUFFER(buf, seed, io.readx.out.nread); + + printf("Trying mincnt > maxcnt\n"); + memset(buf, 0, maxsize); + io.readx.in.offset = 0; + io.readx.in.mincnt = 30000; + io.readx.in.maxcnt = 20000; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readx.out.remaining, 0xFFFF); + CHECK_VALUE(io.readx.out.compaction_mode, 0); + CHECK_VALUE(io.readx.out.nread, io.readx.in.maxcnt); + CHECK_BUFFER(buf, seed, io.readx.out.nread); + + printf("Trying mincnt < maxcnt\n"); + memset(buf, 0, maxsize); + io.readx.in.offset = 0; + io.readx.in.mincnt = 20000; + io.readx.in.maxcnt = 30000; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readx.out.remaining, 0xFFFF); + CHECK_VALUE(io.readx.out.compaction_mode, 0); + CHECK_VALUE(io.readx.out.nread, io.readx.in.maxcnt); + CHECK_BUFFER(buf, seed, io.readx.out.nread); + + printf("Trying locked region\n"); + cli->session->pid++; + if (!cli_lock(cli, fnum, 103, 1, 0, WRITE_LOCK)) { + printf("Failed to lock file at %d\n", __LINE__); + ret = False; + goto done; + } + cli->session->pid--; + memset(buf, 0, maxsize); + io.readx.in.offset = 0; + io.readx.in.mincnt = 100; + io.readx.in.maxcnt = 200; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + +#ifdef LARGE_SMB_OFF_T + printf("Trying large offset read\n"); + io.readx.in.offset = ((SMB_BIG_UINT)0x2) << 32; + io.readx.in.mincnt = 10; + io.readx.in.maxcnt = 10; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readx.out.nread, 0); + + if (!cli_lock64(cli, fnum, io.readx.in.offset, 1, 0, WRITE_LOCK)) { + printf("Failed to lock file at %d\n", __LINE__); + ret = False; + goto done; + } + + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readx.out.nread, 0); +#endif + +done: + cli_close(cli, fnum); + cli_deltree(cli, BASEDIR); + return ret; +} + + +/* + test readbraw ops +*/ +static BOOL test_readbraw(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + union smb_read io; + NTSTATUS status; + BOOL ret = True; + int fnum; + char *buf; + const int maxsize = 90000; + const char *fname = BASEDIR "\\test.txt"; + const char *test_data = "TEST DATA"; + unsigned seed = time(NULL); + + buf = talloc_zero(mem_ctx, maxsize); + + if (cli_deltree(cli, BASEDIR) == -1 || + !cli_mkdir(cli, BASEDIR)) { + printf("Unable to setup %s - %s\n", BASEDIR, cli_errstr(cli)); + return False; + } + + printf("Testing RAW_READ_READBRAW\n"); + + fnum = cli_open(cli, fname, O_RDWR|O_CREAT, DENY_NONE); + if (fnum == -1) { + printf("Failed to create %s - %s\n", fname, cli_errstr(cli)); + ret = False; + goto done; + } + + printf("Trying empty file read\n"); + io.generic.level = RAW_READ_READBRAW; + io.readbraw.in.fnum = fnum; + io.readbraw.in.mincnt = 1; + io.readbraw.in.maxcnt = 1; + io.readbraw.in.offset = 0; + io.readbraw.in.timeout = 0; + io.readbraw.out.data = buf; + status = smb_raw_read(cli->tree, &io); + + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readbraw.out.nread, 0); + + printf("Trying zero file read\n"); + io.readbraw.in.mincnt = 0; + io.readbraw.in.maxcnt = 0; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readbraw.out.nread, 0); + + printf("Trying bad fnum\n"); + io.readbraw.in.fnum = fnum+1; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readbraw.out.nread, 0); + io.readbraw.in.fnum = fnum; + + cli_write(cli, fnum, 0, test_data, 0, strlen(test_data)); + + printf("Trying small read\n"); + io.readbraw.in.fnum = fnum; + io.readbraw.in.offset = 0; + io.readbraw.in.mincnt = strlen(test_data); + io.readbraw.in.maxcnt = strlen(test_data); + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readbraw.out.nread, strlen(test_data)); + if (memcmp(buf, test_data, strlen(test_data)) != 0) { + ret = False; + printf("incorrect data at %d!? (%s:%s)\n", __LINE__, test_data, buf); + goto done; + } + + printf("Trying short read\n"); + io.readbraw.in.offset = 1; + io.readbraw.in.mincnt = strlen(test_data); + io.readbraw.in.maxcnt = strlen(test_data); + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readbraw.out.nread, strlen(test_data)-1); + if (memcmp(buf, test_data+1, strlen(test_data)-1) != 0) { + ret = False; + printf("incorrect data at %d!? (%s:%s)\n", __LINE__, test_data+1, buf); + goto done; + } + + printf("Trying max offset\n"); + io.readbraw.in.offset = ~0; + io.readbraw.in.mincnt = strlen(test_data); + io.readbraw.in.maxcnt = strlen(test_data); + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readbraw.out.nread, 0); + + setup_buffer(buf, seed, maxsize); + cli_write(cli, fnum, 0, buf, 0, maxsize); + memset(buf, 0, maxsize); + + printf("Trying large read\n"); + io.readbraw.in.offset = 0; + io.readbraw.in.mincnt = ~0; + io.readbraw.in.maxcnt = ~0; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readbraw.out.nread, 0xFFFF); + CHECK_BUFFER(buf, seed, io.readbraw.out.nread); + + printf("Trying mincnt > maxcnt\n"); + memset(buf, 0, maxsize); + io.readbraw.in.offset = 0; + io.readbraw.in.mincnt = 30000; + io.readbraw.in.maxcnt = 20000; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readbraw.out.nread, io.readbraw.in.maxcnt); + CHECK_BUFFER(buf, seed, io.readbraw.out.nread); + + printf("Trying mincnt < maxcnt\n"); + memset(buf, 0, maxsize); + io.readbraw.in.offset = 0; + io.readbraw.in.mincnt = 20000; + io.readbraw.in.maxcnt = 30000; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readbraw.out.nread, io.readbraw.in.maxcnt); + CHECK_BUFFER(buf, seed, io.readbraw.out.nread); + + printf("Trying locked region\n"); + cli->session->pid++; + if (!cli_lock(cli, fnum, 103, 1, 0, WRITE_LOCK)) { + printf("Failed to lock file at %d\n", __LINE__); + ret = False; + goto done; + } + cli->session->pid--; + memset(buf, 0, maxsize); + io.readbraw.in.offset = 0; + io.readbraw.in.mincnt = 100; + io.readbraw.in.maxcnt = 200; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readbraw.out.nread, 0); + + printf("Trying locked region with timeout\n"); + memset(buf, 0, maxsize); + io.readbraw.in.offset = 0; + io.readbraw.in.mincnt = 100; + io.readbraw.in.maxcnt = 200; + io.readbraw.in.timeout = 10000; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readbraw.out.nread, 0); + +#ifdef LARGE_SMB_OFF_T + printf("Trying large offset read\n"); + io.readbraw.in.offset = ((SMB_BIG_UINT)0x2) << 32; + io.readbraw.in.mincnt = 10; + io.readbraw.in.maxcnt = 10; + io.readbraw.in.timeout = 0; + status = smb_raw_read(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.readbraw.out.nread, 0); +#endif + +done: + cli_close(cli, fnum); + cli_deltree(cli, BASEDIR); + return ret; +} + + +/* + basic testing of read calls +*/ +BOOL torture_raw_read(int dummy) +{ + struct cli_state *cli; + BOOL ret = True; + TALLOC_CTX *mem_ctx; + + if (!torture_open_connection(&cli)) { + return False; + } + + mem_ctx = talloc_init("torture_raw_read"); + + if (!test_read(cli, mem_ctx)) { + ret = False; + } + + if (!test_readx(cli, mem_ctx)) { + ret = False; + } + + if (!test_lockread(cli, mem_ctx)) { + ret = False; + } + + if (!test_readbraw(cli, mem_ctx)) { + ret = False; + } + + torture_close_connection(cli); + talloc_destroy(mem_ctx); + return ret; +} diff --git a/source4/torture/raw/rename.c b/source4/torture/raw/rename.c new file mode 100644 index 0000000000..4cfa1c95c2 --- /dev/null +++ b/source4/torture/raw/rename.c @@ -0,0 +1,128 @@ +/* + Unix SMB/CIFS implementation. + rename test suite + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + printf("(%d) Incorrect status %s - should be %s\n", \ + __LINE__, nt_errstr(status), nt_errstr(correct)); \ + ret = False; \ + goto done; \ + }} while (0) + +#define BASEDIR "\\testrename" + +/* + test SMBmv ops +*/ +static BOOL test_mv(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + struct smb_rename io; + NTSTATUS status; + BOOL ret = True; + int fnum; + const char *fname1 = BASEDIR "\\test1.txt"; + const char *fname2 = BASEDIR "\\test2.txt"; + + if (cli_deltree(cli, BASEDIR) == -1 || + !cli_mkdir(cli, BASEDIR)) { + printf("Unable to setup %s - %s\n", BASEDIR, cli_errstr(cli)); + return False; + } + + printf("Trying simple rename\n"); + + fnum = create_complex_file(cli, mem_ctx, fname1); + + io.in.pattern1 = fname1; + io.in.pattern2 = fname2; + io.in.attrib = 0; + + status = smb_raw_rename(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION); + + smb_raw_exit(cli->session); + status = smb_raw_rename(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + + printf("trying wildcard rename\n"); + io.in.pattern1 = BASEDIR "\\*.txt"; + io.in.pattern2 = fname1; + + status = smb_raw_rename(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + printf("and again\n"); + status = smb_raw_rename(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + printf("Trying extension change\n"); + io.in.pattern1 = BASEDIR "\\*.txt"; + io.in.pattern2 = BASEDIR "\\*.bak"; + status = smb_raw_rename(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb_raw_rename(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_NO_SUCH_FILE); + + printf("Checking attrib handling\n"); + torture_set_file_attribute(cli->tree, BASEDIR "\\test1.bak", FILE_ATTRIBUTE_HIDDEN); + io.in.pattern1 = BASEDIR "\\test1.bak"; + io.in.pattern2 = BASEDIR "\\*.txt"; + io.in.attrib = 0; + status = smb_raw_rename(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_NO_SUCH_FILE); + + io.in.attrib = FILE_ATTRIBUTE_HIDDEN; + status = smb_raw_rename(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + +done: + smb_raw_exit(cli->session); + cli_deltree(cli, BASEDIR); + return ret; +} + + +/* + basic testing of rename calls +*/ +BOOL torture_raw_rename(int dummy) +{ + struct cli_state *cli; + BOOL ret = True; + TALLOC_CTX *mem_ctx; + + if (!torture_open_connection(&cli)) { + return False; + } + + mem_ctx = talloc_init("torture_raw_rename"); + + if (!test_mv(cli, mem_ctx)) { + ret = False; + } + + torture_close_connection(cli); + talloc_destroy(mem_ctx); + return ret; +} diff --git a/source4/torture/raw/search.c b/source4/torture/raw/search.c new file mode 100644 index 0000000000..6cfdd2b3ff --- /dev/null +++ b/source4/torture/raw/search.c @@ -0,0 +1,610 @@ +/* + Unix SMB/CIFS implementation. + RAW_SEARCH_* individual test suite + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 BASEDIR "\\testsearch" + +/* + callback function for single_search +*/ +static BOOL single_search_callback(void *private, union smb_search_data *file) +{ + union smb_search_data *data = private; + + *data = *file; + + return True; +} + +/* + do a single file (non-wildcard) search +*/ +static NTSTATUS single_search(struct cli_state *cli, + TALLOC_CTX *mem_ctx, + const char *pattern, + enum search_level level, + union smb_search_data *data) +{ + union smb_search_first io; + NTSTATUS status; + + io.generic.level = level; + if (level == RAW_SEARCH_SEARCH) { + io.search_first.in.max_count = 1; + io.search_first.in.search_attrib = 0; + io.search_first.in.pattern = pattern; + } else { + io.t2ffirst.in.search_attrib = 0; + io.t2ffirst.in.max_count = 1; + io.t2ffirst.in.flags = FLAG_TRANS2_FIND_CLOSE; + io.t2ffirst.in.storage_type = 0; + io.t2ffirst.in.pattern = pattern; + } + + status = smb_raw_search_first(cli->tree, mem_ctx, + &io, (void *)data, single_search_callback); + + return status; +} + + +static struct { + const char *name; + enum search_level level; + uint32 capability_mask; + NTSTATUS status; + union smb_search_data data; +} levels[] = { + {"SEARCH", RAW_SEARCH_SEARCH, }, + {"STANDARD", RAW_SEARCH_STANDARD, }, + {"EA_SIZE", RAW_SEARCH_EA_SIZE, }, + {"DIRECTORY_INFO", RAW_SEARCH_DIRECTORY_INFO, }, + {"FULL_DIRECTORY_INFO", RAW_SEARCH_FULL_DIRECTORY_INFO, }, + {"NAME_INFO", RAW_SEARCH_NAME_INFO, }, + {"BOTH_DIRECTORY_INFO", RAW_SEARCH_BOTH_DIRECTORY_INFO, }, + {"LEVEL_261", RAW_SEARCH_261, }, + {"LEVEL_262", RAW_SEARCH_262, }, + {"UNIX_INFO", RAW_SEARCH_UNIX_INFO, CAP_UNIX} +}; + +/* find a level in the table by name */ +static union smb_search_data *find(const char *name) +{ + int i; + for (i=0;itransport->negotiate.capabilities; + + levels[i].status = single_search(cli, mem_ctx, fname, + levels[i].level, &levels[i].data); + + /* see if this server claims to support this level */ + if ((cap & levels[i].capability_mask) != levels[i].capability_mask) { + continue; + } + + printf("testing %s\n", levels[i].name); + + if (!NT_STATUS_IS_OK(levels[i].status)) { + printf("search level %s(%d) failed - %s\n", + levels[i].name, (int)levels[i].level, + nt_errstr(levels[i].status)); + ret = False; + } + } + + /* get the all_info file into to check against */ + all_info.generic.level = RAW_FILEINFO_ALL_INFO; + all_info.generic.in.fname = fname; + status = smb_raw_pathinfo(cli->tree, mem_ctx, &all_info); + if (!NT_STATUS_IS_OK(status)) { + printf("RAW_FILEINFO_ALL_INFO failed - %s\n", nt_errstr(status)); + ret = False; + goto done; + } + + alt_info.generic.level = RAW_FILEINFO_ALT_NAME_INFO; + alt_info.generic.in.fname = fname; + status = smb_raw_pathinfo(cli->tree, mem_ctx, &alt_info); + if (!NT_STATUS_IS_OK(status)) { + printf("RAW_FILEINFO_ALT_NAME_INFO failed - %s\n", nt_errstr(status)); + ret = False; + goto done; + } + + name_info.generic.level = RAW_FILEINFO_NAME_INFO; + name_info.generic.in.fname = fname; + status = smb_raw_pathinfo(cli->tree, mem_ctx, &name_info); + if (!NT_STATUS_IS_OK(status)) { + printf("RAW_FILEINFO_NAME_INFO failed - %s\n", nt_errstr(status)); + ret = False; + goto done; + } + +#define CHECK_VAL(name, sname1, field1, v, sname2, field2) do { \ + s = find(name); \ + if (s) { \ + if (s->sname1.field1 != v.sname2.out.field2) { \ + printf("(%d) %s/%s [%d] != %s/%s [%d]\n", \ + __LINE__, \ + #sname1, #field1, (int)s->sname1.field1, \ + #sname2, #field2, (int)v.sname2.out.field2); \ + ret = False; \ + } \ + }} while (0) + +#define CHECK_TIME(name, sname1, field1, v, sname2, field2) do { \ + s = find(name); \ + if (s) { \ + if (s->sname1.field1 != (~1 & nt_time_to_unix(&v.sname2.out.field2))) { \ + printf("(%d) %s/%s [%s] != %s/%s [%s]\n", \ + __LINE__, \ + #sname1, #field1, time_string(mem_ctx, s->sname1.field1), \ + #sname2, #field2, nt_time_string(mem_ctx, &v.sname2.out.field2)); \ + ret = False; \ + } \ + }} while (0) + +#define CHECK_NTTIME(name, sname1, field1, v, sname2, field2) do { \ + s = find(name); \ + if (s) { \ + if (memcmp(&s->sname1.field1, &v.sname2.out.field2, sizeof(NTTIME))) { \ + printf("(%d) %s/%s [%s] != %s/%s [%s]\n", \ + __LINE__, \ + #sname1, #field1, nt_time_string(mem_ctx, &s->sname1.field1), \ + #sname2, #field2, nt_time_string(mem_ctx, &v.sname2.out.field2)); \ + ret = False; \ + } \ + }} while (0) + +#define CHECK_STR(name, sname1, field1, v, sname2, field2) do { \ + s = find(name); \ + if (s) { \ + if (!s->sname1.field1 || strcmp(s->sname1.field1, v.sname2.out.field2.s)) { \ + printf("(%d) %s/%s [%s] != %s/%s [%s]\n", \ + __LINE__, \ + #sname1, #field1, s->sname1.field1, \ + #sname2, #field2, v.sname2.out.field2.s); \ + ret = False; \ + } \ + }} while (0) + +#define CHECK_WSTR(name, sname1, field1, v, sname2, field2, flags) do { \ + s = find(name); \ + if (s) { \ + if (!s->sname1.field1.s || \ + strcmp(s->sname1.field1.s, v.sname2.out.field2.s) || \ + wire_bad_flags(&s->sname1.field1, flags)) { \ + printf("(%d) %s/%s [%s] != %s/%s [%s]\n", \ + __LINE__, \ + #sname1, #field1, s->sname1.field1.s, \ + #sname2, #field2, v.sname2.out.field2.s); \ + ret = False; \ + } \ + }} while (0) + +#define CHECK_NAME(name, sname1, field1, fname, flags) do { \ + s = find(name); \ + if (s) { \ + if (!s->sname1.field1.s || \ + strcmp(s->sname1.field1.s, fname) || \ + wire_bad_flags(&s->sname1.field1, flags)) { \ + printf("(%d) %s/%s [%s] != %s\n", \ + __LINE__, \ + #sname1, #field1, s->sname1.field1.s, \ + fname); \ + ret = False; \ + } \ + }} while (0) + + /* check that all the results are as expected */ + CHECK_VAL("SEARCH", search, attrib, all_info, all_info, attrib); + CHECK_VAL("STANDARD", standard, attrib, all_info, all_info, attrib); + CHECK_VAL("EA_SIZE", ea_size, attrib, all_info, all_info, attrib); + CHECK_VAL("DIRECTORY_INFO", directory_info, attrib, all_info, all_info, attrib); + CHECK_VAL("FULL_DIRECTORY_INFO", full_directory_info, attrib, all_info, all_info, attrib); + CHECK_VAL("BOTH_DIRECTORY_INFO", both_directory_info, attrib, all_info, all_info, attrib); + CHECK_VAL("LEVEL_261", level_261, attrib, all_info, all_info, attrib); + CHECK_VAL("LEVEL_262", level_262, attrib, all_info, all_info, attrib); + + CHECK_TIME("SEARCH", search, write_time, all_info, all_info, write_time); + CHECK_TIME("STANDARD", standard, write_time, all_info, all_info, write_time); + CHECK_TIME("EA_SIZE", ea_size, write_time, all_info, all_info, write_time); + CHECK_TIME("STANDARD", standard, create_time, all_info, all_info, create_time); + CHECK_TIME("EA_SIZE", ea_size, create_time, all_info, all_info, create_time); + CHECK_TIME("STANDARD", standard, access_time, all_info, all_info, access_time); + CHECK_TIME("EA_SIZE", ea_size, access_time, all_info, all_info, access_time); + + CHECK_NTTIME("DIRECTORY_INFO", directory_info, write_time, all_info, all_info, write_time); + CHECK_NTTIME("FULL_DIRECTORY_INFO", full_directory_info, write_time, all_info, all_info, write_time); + CHECK_NTTIME("BOTH_DIRECTORY_INFO", both_directory_info, write_time, all_info, all_info, write_time); + CHECK_NTTIME("LEVEL_261", level_261, write_time, all_info, all_info, write_time); + CHECK_NTTIME("LEVEL_262", level_262, write_time, all_info, all_info, write_time); + + CHECK_NTTIME("DIRECTORY_INFO", directory_info, create_time, all_info, all_info, create_time); + CHECK_NTTIME("FULL_DIRECTORY_INFO", full_directory_info, create_time, all_info, all_info, create_time); + CHECK_NTTIME("BOTH_DIRECTORY_INFO", both_directory_info, create_time, all_info, all_info, create_time); + CHECK_NTTIME("LEVEL_261", level_261, create_time, all_info, all_info, create_time); + CHECK_NTTIME("LEVEL_262", level_262, create_time, all_info, all_info, create_time); + + CHECK_NTTIME("DIRECTORY_INFO", directory_info, access_time, all_info, all_info, access_time); + CHECK_NTTIME("FULL_DIRECTORY_INFO", full_directory_info, access_time, all_info, all_info, access_time); + CHECK_NTTIME("BOTH_DIRECTORY_INFO", both_directory_info, access_time, all_info, all_info, access_time); + CHECK_NTTIME("LEVEL_261", level_261, access_time, all_info, all_info, access_time); + CHECK_NTTIME("LEVEL_262", level_262, access_time, all_info, all_info, access_time); + + CHECK_NTTIME("DIRECTORY_INFO", directory_info, create_time, all_info, all_info, create_time); + CHECK_NTTIME("FULL_DIRECTORY_INFO", full_directory_info, create_time, all_info, all_info, create_time); + CHECK_NTTIME("BOTH_DIRECTORY_INFO", both_directory_info, create_time, all_info, all_info, create_time); + CHECK_NTTIME("LEVEL_261", level_261, create_time, all_info, all_info, create_time); + CHECK_NTTIME("LEVEL_262", level_262, create_time, all_info, all_info, create_time); + + CHECK_VAL("SEARCH", search, size, all_info, all_info, size); + CHECK_VAL("STANDARD", standard, size, all_info, all_info, size); + CHECK_VAL("EA_SIZE", ea_size, size, all_info, all_info, size); + CHECK_VAL("DIRECTORY_INFO", directory_info, size, all_info, all_info, size); + CHECK_VAL("FULL_DIRECTORY_INFO", full_directory_info, size, all_info, all_info, size); + CHECK_VAL("BOTH_DIRECTORY_INFO", both_directory_info, size, all_info, all_info, size); + CHECK_VAL("LEVEL_261", level_261, size, all_info, all_info, size); + CHECK_VAL("LEVEL_262", level_262, size, all_info, all_info, size); + + CHECK_VAL("STANDARD", standard, alloc_size, all_info, all_info, alloc_size); + CHECK_VAL("EA_SIZE", ea_size, alloc_size, all_info, all_info, alloc_size); + CHECK_VAL("DIRECTORY_INFO", directory_info, alloc_size, all_info, all_info, alloc_size); + CHECK_VAL("FULL_DIRECTORY_INFO", full_directory_info, alloc_size, all_info, all_info, alloc_size); + CHECK_VAL("BOTH_DIRECTORY_INFO", both_directory_info, alloc_size, all_info, all_info, alloc_size); + CHECK_VAL("LEVEL_261", level_261, alloc_size, all_info, all_info, alloc_size); + CHECK_VAL("LEVEL_262", level_262, alloc_size, all_info, all_info, alloc_size); + + CHECK_VAL("EA_SIZE", ea_size, ea_size, all_info, all_info, ea_size); + CHECK_VAL("FULL_DIRECTORY_INFO", full_directory_info, ea_size, all_info, all_info, ea_size); + CHECK_VAL("BOTH_DIRECTORY_INFO", both_directory_info, ea_size, all_info, all_info, ea_size); + CHECK_VAL("LEVEL_261", level_261, ea_size, all_info, all_info, ea_size); + CHECK_VAL("LEVEL_262", level_262, ea_size, all_info, all_info, ea_size); + + CHECK_STR("SEARCH", search, name, alt_info, alt_name_info, fname); + CHECK_WSTR("BOTH_DIRECTORY_INFO", both_directory_info, short_name, alt_info, alt_name_info, fname, STR_UNICODE); + + CHECK_NAME("STANDARD", standard, name, fname+1, 0); + CHECK_NAME("EA_SIZE", ea_size, name, fname+1, 0); + CHECK_NAME("DIRECTORY_INFO", directory_info, name, fname+1, STR_TERMINATE_ASCII); + CHECK_NAME("FULL_DIRECTORY_INFO", full_directory_info, name, fname+1, STR_TERMINATE_ASCII); + CHECK_NAME("NAME_INFO", name_info, name, fname+1, STR_TERMINATE_ASCII); + CHECK_NAME("BOTH_DIRECTORY_INFO", both_directory_info, name, fname+1, STR_TERMINATE_ASCII); + CHECK_NAME("LEVEL_261", level_261, name, fname+1, STR_TERMINATE_ASCII); + CHECK_NAME("LEVEL_262", level_262, name, fname+1, STR_TERMINATE_ASCII); + +done: + smb_raw_exit(cli->session); + cli_unlink(cli, fname); + + return ret; +} + + +struct multiple_result { + TALLOC_CTX *mem_ctx; + int count; + union smb_search_data *list; +}; + +/* + callback function for multiple_search +*/ +static BOOL multiple_search_callback(void *private, union smb_search_data *file) +{ + struct multiple_result *data = private; + + + data->count++; + data->list = talloc_realloc(data->mem_ctx, + data->list, + data->count * (sizeof(data->list[0]))); + + data->list[data->count-1] = *file; + + return True; +} + +enum continue_type {CONT_FLAGS, CONT_NAME, CONT_RESUME_KEY}; + +/* + do a single file (non-wildcard) search +*/ +static NTSTATUS multiple_search(struct cli_state *cli, + TALLOC_CTX *mem_ctx, + const char *pattern, + enum search_level level, + enum continue_type cont_type, + void *data) +{ + union smb_search_first io; + union smb_search_next io2; + NTSTATUS status; + const int per_search = 300; + struct multiple_result *result = data; + + io.generic.level = level; + if (level == RAW_SEARCH_SEARCH) { + io.search_first.in.max_count = per_search; + io.search_first.in.search_attrib = 0; + io.search_first.in.pattern = pattern; + } else { + io.t2ffirst.in.search_attrib = 0; + io.t2ffirst.in.max_count = per_search; + io.t2ffirst.in.flags = 0; + io.t2ffirst.in.storage_type = 0; + io.t2ffirst.in.pattern = pattern; + if (cont_type == CONT_RESUME_KEY) { + io.t2ffirst.in.flags = FLAG_TRANS2_FIND_REQUIRE_RESUME | + FLAG_TRANS2_FIND_BACKUP_INTENT; + } + } + + status = smb_raw_search_first(cli->tree, mem_ctx, + &io, data, multiple_search_callback); + + + while (NT_STATUS_IS_OK(status)) { + io2.generic.level = level; + if (level == RAW_SEARCH_SEARCH) { + io2.search_next.in.max_count = per_search; + io2.search_next.in.search_attrib = 0; + io2.search_next.in.search_id = result->list[result->count-1].search.search_id; + } else { + io2.t2fnext.in.handle = io.t2ffirst.out.handle; + io2.t2fnext.in.max_count = per_search; + io2.t2fnext.in.resume_key = 0; + io2.t2fnext.in.flags = 0; + io2.t2fnext.in.last_name = ""; + switch (cont_type) { + case CONT_RESUME_KEY: + if (level == RAW_SEARCH_STANDARD) { + io2.t2fnext.in.resume_key = + result->list[result->count-1].standard.resume_key; + } else { + io2.t2fnext.in.resume_key = + result->list[result->count-1].both_directory_info.file_index; + } + io2.t2fnext.in.flags = FLAG_TRANS2_FIND_REQUIRE_RESUME | + FLAG_TRANS2_FIND_BACKUP_INTENT; + break; + case CONT_NAME: + if (level == RAW_SEARCH_STANDARD) { + io2.t2fnext.in.last_name = + result->list[result->count-1].standard.name.s; + } else { + io2.t2fnext.in.last_name = + result->list[result->count-1].both_directory_info.name.s; + } + break; + case CONT_FLAGS: + io2.t2fnext.in.flags = FLAG_TRANS2_FIND_CONTINUE; + break; + } + } + + status = smb_raw_search_next(cli->tree, mem_ctx, + &io2, data, multiple_search_callback); + if (!NT_STATUS_IS_OK(status)) { + break; + } + if (level == RAW_SEARCH_SEARCH) { + if (io2.search_next.out.count == 0) { + break; + } + } else if (io2.t2fnext.out.count == 0 || + io2.t2fnext.out.end_of_search) { + break; + } + } + + return status; +} + +#define CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + printf("(%d) Incorrect status %s - should be %s\n", \ + __LINE__, nt_errstr(status), nt_errstr(correct)); \ + ret = False; \ + goto done; \ + }} while (0) + +#define CHECK_VALUE(v, correct) do { \ + if ((v) != (correct)) { \ + printf("(%d) Incorrect value %s=%d - should be %d\n", \ + __LINE__, #v, v, correct); \ + ret = False; \ + }} while (0) + + +static int search_both_compare(union smb_search_data *d1, union smb_search_data *d2) +{ + return strcmp(d1->both_directory_info.name.s, d2->both_directory_info.name.s); +} + +static int search_standard_compare(union smb_search_data *d1, union smb_search_data *d2) +{ + return strcmp(d1->standard.name.s, d2->standard.name.s); +} + +static int search_old_compare(union smb_search_data *d1, union smb_search_data *d2) +{ + return strcmp(d1->search.name, d2->search.name); +} + + +/* + basic testing of search calls using many files +*/ +static BOOL test_many_files(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + const int num_files = 700; + int i, fnum, t; + char *fname; + BOOL ret = True; + NTSTATUS status; + struct multiple_result result; + struct { + const char *name; + const char *cont_name; + enum search_level level; + enum continue_type cont_type; + } search_types[] = { + {"BOTH_DIRECTORY_INFO", "FLAGS", RAW_SEARCH_BOTH_DIRECTORY_INFO, CONT_FLAGS}, + {"BOTH_DIRECTORY_INFO", "KEY", RAW_SEARCH_BOTH_DIRECTORY_INFO, CONT_RESUME_KEY}, + {"BOTH_DIRECTORY_INFO", "NAME", RAW_SEARCH_BOTH_DIRECTORY_INFO, CONT_NAME}, + {"STANDARD", "FLAGS", RAW_SEARCH_STANDARD, CONT_FLAGS}, + {"STANDARD", "KEY", RAW_SEARCH_STANDARD, CONT_RESUME_KEY}, + {"STANDARD", "NAME", RAW_SEARCH_STANDARD, CONT_NAME}, + {"SEARCH", "ID", RAW_SEARCH_SEARCH, CONT_RESUME_KEY} + }; + + if (cli_deltree(cli, BASEDIR) == -1 || + !cli_mkdir(cli, BASEDIR)) { + printf("Failed to create " BASEDIR " - %s\n", cli_errstr(cli)); + return False; + } + + printf("Creating %d files\n", num_files); + + for (i=0;isession); + cli_deltree(cli, BASEDIR); + + return ret; +} + + +/* + basic testing of all RAW_SEARCH_* calls using a single file +*/ +BOOL torture_raw_search(int dummy) +{ + struct cli_state *cli; + BOOL ret = True; + TALLOC_CTX *mem_ctx; + + if (!torture_open_connection(&cli)) { + return False; + } + + mem_ctx = talloc_init("torture_search"); + + if (!test_one_file(cli, mem_ctx)) { + ret = False; + } + + if (!test_many_files(cli, mem_ctx)) { + ret = False; + } + + torture_close_connection(cli); + talloc_destroy(mem_ctx); + + return ret; +} diff --git a/source4/torture/raw/seek.c b/source4/torture/raw/seek.c new file mode 100644 index 0000000000..9379b676ab --- /dev/null +++ b/source4/torture/raw/seek.c @@ -0,0 +1,152 @@ +/* + Unix SMB/CIFS implementation. + seek test suite + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + printf("(%d) Incorrect status %s - should be %s\n", \ + __LINE__, nt_errstr(status), nt_errstr(correct)); \ + ret = False; \ + goto done; \ + }} while (0) + +#define CHECK_VALUE(v, correct) do { \ + if ((v) != (correct)) { \ + printf("(%d) Incorrect value %s=%d - should be %d\n", \ + __LINE__, #v, v, correct); \ + ret = False; \ + goto done; \ + }} while (0) + +#define BASEDIR "\\testseek" + +/* + test seek ops +*/ +static BOOL test_seek(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + struct smb_seek io; + union smb_fileinfo finfo; + NTSTATUS status; + BOOL ret = True; + int fnum; + const char *fname = BASEDIR "\\test.txt"; + + if (cli_deltree(cli, BASEDIR) == -1 || + !cli_mkdir(cli, BASEDIR)) { + printf("Unable to setup %s - %s\n", BASEDIR, cli_errstr(cli)); + return False; + } + + fnum = create_complex_file(cli, mem_ctx, fname); + if (fnum == -1) { + printf("Failed to open test.txt - %s\n", cli_errstr(cli)); + ret = False; + goto done; + } + + finfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + finfo.position_information.in.fnum = fnum; + + printf("Trying bad handle\n"); + io.in.fnum = fnum+1; + io.in.mode = SEEK_MODE_START; + io.in.offset = 0; + status = smb_raw_seek(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + + printf("Trying simple seek\n"); + io.in.fnum = fnum; + io.in.mode = SEEK_MODE_START; + io.in.offset = 17; + status = smb_raw_seek(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.out.offset, 17); + status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(finfo.position_information.out.position, 0); + + printf("Trying relative seek\n"); + io.in.fnum = fnum; + io.in.mode = SEEK_MODE_CURRENT; + io.in.offset = -3; + status = smb_raw_seek(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.out.offset, 14); + + printf("Trying end seek\n"); + io.in.fnum = fnum; + io.in.mode = SEEK_MODE_END; + io.in.offset = 0; + status = smb_raw_seek(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + finfo.generic.level = RAW_FILEINFO_ALL_INFO; + finfo.all_info.in.fnum = fnum; + status = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.out.offset, finfo.all_info.out.size); + + printf("Trying max seek\n"); + io.in.fnum = fnum; + io.in.mode = SEEK_MODE_START; + io.in.offset = -1; + status = smb_raw_seek(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.out.offset, 0xffffffff); + + printf("Trying max overflow\n"); + io.in.fnum = fnum; + io.in.mode = SEEK_MODE_CURRENT; + io.in.offset = 1000; + status = smb_raw_seek(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.out.offset, 999); + +done: + smb_raw_exit(cli->session); + cli_deltree(cli, BASEDIR); + return ret; +} + + +/* + basic testing of seek calls +*/ +BOOL torture_raw_seek(int dummy) +{ + struct cli_state *cli; + BOOL ret = True; + TALLOC_CTX *mem_ctx; + + if (!torture_open_connection(&cli)) { + return False; + } + + mem_ctx = talloc_init("torture_raw_seek"); + + if (!test_seek(cli, mem_ctx)) { + ret = False; + } + + torture_close_connection(cli); + talloc_destroy(mem_ctx); + return ret; +} diff --git a/source4/torture/raw/setfileinfo.c b/source4/torture/raw/setfileinfo.c new file mode 100644 index 0000000000..c169895020 --- /dev/null +++ b/source4/torture/raw/setfileinfo.c @@ -0,0 +1,498 @@ +/* + Unix SMB/CIFS implementation. + RAW_SFILEINFO_* individual test suite + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 BASEDIR "\\testsfileinfo" + +/* basic testing of all RAW_SFILEINFO_* calls + for each call we test that it succeeds, and where possible test + for consistency between the calls. +*/ +BOOL torture_raw_sfileinfo(int dummy) +{ + struct cli_state *cli; + BOOL ret = True; + TALLOC_CTX *mem_ctx; + int fnum = -1; + char *fnum_fname; + char *fnum_fname_new; + char *path_fname; + char *path_fname_new; + union smb_fileinfo finfo1, finfo2; + union smb_setfileinfo sfinfo; + NTSTATUS status, status2; + const char *call_name; + time_t basetime = (time(NULL) - 86400) & ~1; + BOOL check_fnum; + int n = time(NULL) % 100; + + asprintf(&path_fname, BASEDIR "\\fname_test_%d.txt", n); + asprintf(&path_fname_new, BASEDIR "\\fname_test_new_%d.txt", n); + asprintf(&fnum_fname, BASEDIR "\\fnum_test_%d.txt", n); + asprintf(&fnum_fname_new, BASEDIR "\\fnum_test_new_%d.txt", n); + + if (!torture_open_connection(&cli)) { + return False; + } + + mem_ctx = talloc_init("torture_sfileinfo"); + + cli_deltree(cli, BASEDIR); + cli_mkdir(cli, BASEDIR); + +#define RECREATE_FILE(fname) do { \ + if (fnum != -1) cli_close(cli, fnum); \ + fnum = create_complex_file(cli, mem_ctx, fname); \ + if (fnum == -1) { \ + printf("(%d) ERROR: open of %s failed (%s)\n", \ + __LINE__, fname, cli_errstr(cli)); \ + ret = False; \ + goto done; \ + }} while (0) + +#define RECREATE_BOTH do { \ + RECREATE_FILE(path_fname); \ + cli_close(cli, fnum); \ + RECREATE_FILE(fnum_fname); \ + } while (0) + + RECREATE_BOTH; + +#define CHECK_CALL_FNUM(call, rightstatus) do { \ + check_fnum = True; \ + call_name = #call; \ + sfinfo.generic.level = RAW_SFILEINFO_ ## call; \ + sfinfo.generic.file.fnum = fnum; \ + status = smb_raw_setfileinfo(cli->tree, &sfinfo); \ + if (!NT_STATUS_EQUAL(status, rightstatus)) { \ + printf("(%d) %s - %s (should be %s)\n", __LINE__, #call, \ + nt_errstr(status), nt_errstr(rightstatus)); \ + ret = False; \ + } \ + finfo1.generic.level = RAW_FILEINFO_ALL_INFO; \ + finfo1.generic.in.fnum = fnum; \ + status2 = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo1); \ + if (!NT_STATUS_IS_OK(status2)) { \ + printf("(%d) %s pathinfo - %s\n", __LINE__, #call, nt_errstr(status)); \ + ret = False; \ + }} while (0) + +#define CHECK_CALL_PATH(call, rightstatus) do { \ + check_fnum = False; \ + call_name = #call; \ + sfinfo.generic.level = RAW_SFILEINFO_ ## call; \ + sfinfo.generic.file.fname = path_fname; \ + status = smb_raw_setpathinfo(cli->tree, &sfinfo); \ + if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { \ + sfinfo.generic.file.fname = path_fname_new; \ + status = smb_raw_setpathinfo(cli->tree, &sfinfo); \ + } \ + if (!NT_STATUS_EQUAL(status, rightstatus)) { \ + printf("(%d) %s - %s (should be %s)\n", __LINE__, #call, \ + nt_errstr(status), nt_errstr(rightstatus)); \ + ret = False; \ + } \ + finfo1.generic.level = RAW_FILEINFO_ALL_INFO; \ + finfo1.generic.in.fname = path_fname; \ + status2 = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo1); \ + if (NT_STATUS_EQUAL(status2, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { \ + finfo1.generic.in.fname = path_fname_new; \ + status2 = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo1); \ + } \ + if (!NT_STATUS_IS_OK(status2)) { \ + printf("(%d) %s pathinfo - %s\n", __LINE__, #call, nt_errstr(status2)); \ + ret = False; \ + }} while (0) + +#define CHECK1(call) \ + do { if (NT_STATUS_IS_OK(status)) { \ + finfo2.generic.level = RAW_FILEINFO_ ## call; \ + if (check_fnum) { \ + finfo2.generic.in.fnum = fnum; \ + status2 = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo2); \ + } else { \ + finfo2.generic.in.fname = path_fname; \ + status2 = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo2); \ + if (NT_STATUS_EQUAL(status2, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { \ + finfo2.generic.in.fname = path_fname_new; \ + status2 = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo2); \ + } \ + } \ + if (!NT_STATUS_IS_OK(status2)) { \ + printf("%s - %s\n", #call, nt_errstr(status2)); \ + } \ + }} while (0) + +#define CHECK_VALUE(call, stype, field, value) do { \ + CHECK1(call); \ + if (NT_STATUS_IS_OK(status) && finfo2.stype.out.field != value) { \ + printf("(%d) %s - %s/%s should be 0x%x - 0x%x\n", __LINE__, \ + call_name, #stype, #field, \ + (uint_t)value, (uint_t)finfo2.stype.out.field); \ + dump_all_info(mem_ctx, &finfo1); \ + }} while (0) + +#define CHECK_TIME(call, stype, field, value) do { \ + CHECK1(call); \ + if (NT_STATUS_IS_OK(status) && nt_time_to_unix(&finfo2.stype.out.field) != value) { \ + printf("(%d) %s - %s/%s should be 0x%x - 0x%x\n", __LINE__, \ + call_name, #stype, #field, \ + (uint_t)value, \ + (uint_t)nt_time_to_unix(&finfo2.stype.out.field)); \ + printf("\t%s", http_timestring(mem_ctx, value)); \ + printf("\t%s\n", nt_time_string(mem_ctx, &finfo2.stype.out.field)); \ + dump_all_info(mem_ctx, &finfo1); \ + }} while (0) + +#define CHECK_STR(call, stype, field, value) do { \ + CHECK1(call); \ + if (NT_STATUS_IS_OK(status) && strcmp(finfo2.stype.out.field, value) != 0) { \ + printf("(%d) %s - %s/%s should be '%s' - '%s'\n", __LINE__, \ + call_name, #stype, #field, \ + value, \ + finfo2.stype.out.field); \ + dump_all_info(mem_ctx, &finfo1); \ + }} while (0) + + + printf("test setattr\n"); + sfinfo.setattr.in.attrib = FILE_ATTRIBUTE_READONLY; + sfinfo.setattr.in.write_time = basetime; + CHECK_CALL_PATH(SETATTR, NT_STATUS_OK); + CHECK_VALUE (ALL_INFO, all_info, attrib, FILE_ATTRIBUTE_READONLY); + CHECK_TIME (ALL_INFO, all_info, write_time, basetime); + + printf("setting to NORMAL doesn't do anything\n"); + sfinfo.setattr.in.attrib = FILE_ATTRIBUTE_NORMAL; + sfinfo.setattr.in.write_time = 0; + CHECK_CALL_PATH(SETATTR, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, attrib, FILE_ATTRIBUTE_READONLY); + CHECK_TIME (ALL_INFO, all_info, write_time, basetime); + + printf("a zero write_time means don't change\n"); + sfinfo.setattr.in.attrib = 0; + sfinfo.setattr.in.write_time = 0; + CHECK_CALL_PATH(SETATTR, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, attrib, FILE_ATTRIBUTE_NORMAL); + CHECK_TIME (ALL_INFO, all_info, write_time, basetime); + + printf("test setattre\n"); + sfinfo.setattre.in.create_time = basetime + 20; + sfinfo.setattre.in.access_time = basetime + 30; + sfinfo.setattre.in.write_time = basetime + 40; + CHECK_CALL_FNUM(SETATTRE, NT_STATUS_OK); + CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 20); + CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 30); + CHECK_TIME(ALL_INFO, all_info, write_time, basetime + 40); + + sfinfo.setattre.in.create_time = 0; + sfinfo.setattre.in.access_time = 0; + sfinfo.setattre.in.write_time = 0; + CHECK_CALL_FNUM(SETATTRE, NT_STATUS_OK); + CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 20); + CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 30); + CHECK_TIME(ALL_INFO, all_info, write_time, basetime + 40); + + printf("test standard level\n"); + sfinfo.standard.in.create_time = basetime + 100; + sfinfo.standard.in.access_time = basetime + 200; + sfinfo.standard.in.write_time = basetime + 300; + CHECK_CALL_FNUM(STANDARD, NT_STATUS_OK); + CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 100); + CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 200); + CHECK_TIME(ALL_INFO, all_info, write_time, basetime + 300); + + printf("test basic_info level\n"); + basetime += 86400; + unix_to_nt_time(&sfinfo.basic_info.in.create_time, basetime + 100); + unix_to_nt_time(&sfinfo.basic_info.in.access_time, basetime + 200); + unix_to_nt_time(&sfinfo.basic_info.in.write_time, basetime + 300); + unix_to_nt_time(&sfinfo.basic_info.in.change_time, basetime + 400); + sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_READONLY; + CHECK_CALL_FNUM(BASIC_INFO, NT_STATUS_OK); + CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 100); + CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 200); + CHECK_TIME(ALL_INFO, all_info, write_time, basetime + 300); + CHECK_TIME(ALL_INFO, all_info, change_time, basetime + 400); + CHECK_VALUE(ALL_INFO, all_info, attrib, FILE_ATTRIBUTE_READONLY); + + printf("a zero time means don't change\n"); + unix_to_nt_time(&sfinfo.basic_info.in.create_time, 0); + unix_to_nt_time(&sfinfo.basic_info.in.access_time, 0); + unix_to_nt_time(&sfinfo.basic_info.in.write_time, 0); + unix_to_nt_time(&sfinfo.basic_info.in.change_time, 0); + sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_NORMAL; + CHECK_CALL_FNUM(BASIC_INFO, NT_STATUS_OK); + CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 100); + CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 200); + CHECK_TIME(ALL_INFO, all_info, write_time, basetime + 300); + CHECK_TIME(ALL_INFO, all_info, change_time, basetime + 400); + CHECK_VALUE(ALL_INFO, all_info, attrib, FILE_ATTRIBUTE_NORMAL); + + printf("test basic_information level\n"); + basetime += 86400; + unix_to_nt_time(&sfinfo.basic_info.in.create_time, basetime + 100); + unix_to_nt_time(&sfinfo.basic_info.in.access_time, basetime + 200); + unix_to_nt_time(&sfinfo.basic_info.in.write_time, basetime + 300); + unix_to_nt_time(&sfinfo.basic_info.in.change_time, basetime + 400); + sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_READONLY; + CHECK_CALL_FNUM(BASIC_INFORMATION, NT_STATUS_OK); + CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 100); + CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 200); + CHECK_TIME(ALL_INFO, all_info, write_time, basetime + 300); + CHECK_TIME(ALL_INFO, all_info, change_time, basetime + 400); + CHECK_VALUE(ALL_INFO, all_info, attrib, FILE_ATTRIBUTE_READONLY); + + CHECK_CALL_PATH(BASIC_INFORMATION, NT_STATUS_OK); + CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 100); + CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 200); + CHECK_TIME(ALL_INFO, all_info, write_time, basetime + 300); + CHECK_TIME(ALL_INFO, all_info, change_time, basetime + 400); + CHECK_VALUE(ALL_INFO, all_info, attrib, FILE_ATTRIBUTE_READONLY); + + printf("a zero time means don't change\n"); + unix_to_nt_time(&sfinfo.basic_info.in.create_time, 0); + unix_to_nt_time(&sfinfo.basic_info.in.access_time, 0); + unix_to_nt_time(&sfinfo.basic_info.in.write_time, 0); + unix_to_nt_time(&sfinfo.basic_info.in.change_time, 0); + sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_NORMAL; + CHECK_CALL_FNUM(BASIC_INFORMATION, NT_STATUS_OK); + CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 100); + CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 200); + CHECK_TIME(ALL_INFO, all_info, write_time, basetime + 300); + CHECK_TIME(ALL_INFO, all_info, change_time, basetime + 400); + CHECK_VALUE(ALL_INFO, all_info, attrib, FILE_ATTRIBUTE_NORMAL); + + CHECK_CALL_PATH(BASIC_INFORMATION, NT_STATUS_OK); + CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 100); + CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 200); + CHECK_TIME(ALL_INFO, all_info, write_time, basetime + 300); + + /* interesting - w2k3 leaves change_time as current time for 0 change time + in setpathinfo + CHECK_TIME(ALL_INFO, all_info, change_time, basetime + 400); + */ + CHECK_VALUE(ALL_INFO, all_info, attrib, FILE_ATTRIBUTE_NORMAL); + + printf("test disposition_info level\n"); + sfinfo.disposition_info.in.delete_on_close = 1; + CHECK_CALL_FNUM(DISPOSITION_INFO, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, delete_pending, 1); + CHECK_VALUE(ALL_INFO, all_info, nlink, 0); + + sfinfo.disposition_info.in.delete_on_close = 0; + CHECK_CALL_FNUM(DISPOSITION_INFO, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, delete_pending, 0); + CHECK_VALUE(ALL_INFO, all_info, nlink, 1); + + printf("test disposition_information level\n"); + sfinfo.disposition_info.in.delete_on_close = 1; + CHECK_CALL_FNUM(DISPOSITION_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, delete_pending, 1); + CHECK_VALUE(ALL_INFO, all_info, nlink, 0); + + /* this would delete the file! */ + /* + CHECK_CALL_PATH(DISPOSITION_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, delete_pending, 1); + CHECK_VALUE(ALL_INFO, all_info, nlink, 0); + */ + + sfinfo.disposition_info.in.delete_on_close = 0; + CHECK_CALL_FNUM(DISPOSITION_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, delete_pending, 0); + CHECK_VALUE(ALL_INFO, all_info, nlink, 1); + + CHECK_CALL_PATH(DISPOSITION_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, delete_pending, 0); + CHECK_VALUE(ALL_INFO, all_info, nlink, 1); + + printf("test allocation_info level\n"); + sfinfo.allocation_info.in.alloc_size = 0; + CHECK_CALL_FNUM(ALLOCATION_INFO, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, size, 0); + CHECK_VALUE(ALL_INFO, all_info, alloc_size, 0); + + sfinfo.allocation_info.in.alloc_size = 4096; + CHECK_CALL_FNUM(ALLOCATION_INFO, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, alloc_size, 4096); + CHECK_VALUE(ALL_INFO, all_info, size, 0); + + RECREATE_BOTH; + sfinfo.allocation_info.in.alloc_size = 0; + CHECK_CALL_FNUM(ALLOCATION_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, size, 0); + CHECK_VALUE(ALL_INFO, all_info, alloc_size, 0); + + CHECK_CALL_PATH(ALLOCATION_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, size, 0); + CHECK_VALUE(ALL_INFO, all_info, alloc_size, 0); + + sfinfo.allocation_info.in.alloc_size = 4096; + CHECK_CALL_FNUM(ALLOCATION_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, alloc_size, 4096); + CHECK_VALUE(ALL_INFO, all_info, size, 0); + + /* setting the allocation size up via setpathinfo seems + to be broken in w2k3 */ + CHECK_CALL_PATH(ALLOCATION_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, alloc_size, 0); + CHECK_VALUE(ALL_INFO, all_info, size, 0); + + printf("test end_of_file_info level\n"); + sfinfo.end_of_file_info.in.size = 37; + CHECK_CALL_FNUM(END_OF_FILE_INFO, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, size, 37); + + sfinfo.end_of_file_info.in.size = 7; + CHECK_CALL_FNUM(END_OF_FILE_INFO, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, size, 7); + + sfinfo.end_of_file_info.in.size = 37; + CHECK_CALL_FNUM(END_OF_FILE_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, size, 37); + + CHECK_CALL_PATH(END_OF_FILE_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, size, 37); + + sfinfo.end_of_file_info.in.size = 7; + CHECK_CALL_FNUM(END_OF_FILE_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, size, 7); + + CHECK_CALL_PATH(END_OF_FILE_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, size, 7); + + printf("test position_information level\n"); + sfinfo.position_information.in.position = 123456; + CHECK_CALL_FNUM(POSITION_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(POSITION_INFORMATION, position_information, position, 123456); + + CHECK_CALL_PATH(POSITION_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(POSITION_INFORMATION, position_information, position, 0); + + printf("test mode_information level\n"); + sfinfo.mode_information.in.mode = 2; + CHECK_CALL_FNUM(MODE_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(MODE_INFORMATION, mode_information, mode, 2); + + CHECK_CALL_PATH(MODE_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(MODE_INFORMATION, mode_information, mode, 0); + + sfinfo.mode_information.in.mode = 1; + CHECK_CALL_FNUM(MODE_INFORMATION, NT_STATUS_INVALID_PARAMETER); + CHECK_CALL_PATH(MODE_INFORMATION, NT_STATUS_INVALID_PARAMETER); + + sfinfo.mode_information.in.mode = 0; + CHECK_CALL_FNUM(MODE_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(MODE_INFORMATION, mode_information, mode, 0); + + CHECK_CALL_PATH(MODE_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(MODE_INFORMATION, mode_information, mode, 0); + + printf("finally the rename_information level\n"); + cli_close(cli, create_complex_file(cli, mem_ctx, fnum_fname_new)); + cli_close(cli, create_complex_file(cli, mem_ctx, path_fname_new)); + + sfinfo.rename_information.in.overwrite = 0; + sfinfo.rename_information.in.root_fid = 0; + sfinfo.rename_information.in.new_name = fnum_fname_new+strlen(BASEDIR)+1; + CHECK_CALL_FNUM(RENAME_INFORMATION, NT_STATUS_OBJECT_NAME_COLLISION); + + sfinfo.rename_information.in.new_name = path_fname_new+strlen(BASEDIR)+1; + CHECK_CALL_PATH(RENAME_INFORMATION, NT_STATUS_OBJECT_NAME_COLLISION); + + sfinfo.rename_information.in.new_name = fnum_fname_new+strlen(BASEDIR)+1; + sfinfo.rename_information.in.overwrite = 1; + CHECK_CALL_FNUM(RENAME_INFORMATION, NT_STATUS_OK); + CHECK_STR(NAME_INFO, name_info, fname.s, fnum_fname_new); + + sfinfo.rename_information.in.new_name = path_fname_new+strlen(BASEDIR)+1; + CHECK_CALL_PATH(RENAME_INFORMATION, NT_STATUS_OK); + CHECK_STR(NAME_INFO, name_info, fname.s, path_fname_new); + + sfinfo.rename_information.in.new_name = fnum_fname+strlen(BASEDIR)+1; + CHECK_CALL_FNUM(RENAME_INFORMATION, NT_STATUS_OK); + CHECK_STR(NAME_INFO, name_info, fname.s, fnum_fname); + + sfinfo.rename_information.in.new_name = path_fname+strlen(BASEDIR)+1; + CHECK_CALL_PATH(RENAME_INFORMATION, NT_STATUS_OK); + CHECK_STR(NAME_INFO, name_info, fname.s, path_fname); + +#if 0 + printf("test unix_basic level\n"); + CHECK_CALL_FNUM(UNIX_BASIC, NT_STATUS_OK); + CHECK_CALL_PATH(UNIX_BASIC, NT_STATUS_OK); + + printf("test unix_link level\n"); + CHECK_CALL_FNUM(UNIX_LINK, NT_STATUS_OK); + CHECK_CALL_PATH(UNIX_LINK, NT_STATUS_OK); +#endif + +done: + cli_close(cli, fnum); + if (!cli_unlink(cli, fnum_fname)) { + printf("Failed to delete %s - %s\n", fnum_fname, cli_errstr(cli)); + } + if (!cli_unlink(cli, path_fname)) { + printf("Failed to delete %s - %s\n", path_fname, cli_errstr(cli)); + } + + torture_close_connection(cli); + talloc_destroy(mem_ctx); + return ret; +} + + +/* + look for the w2k3 setpathinfo STANDARD bug +*/ +BOOL torture_raw_sfileinfo_bug(int dummy) +{ + struct cli_state *cli; + TALLOC_CTX *mem_ctx; + const char *fname = "\\bug3.txt"; + union smb_setfileinfo sfinfo; + NTSTATUS status; + int fnum; + + if (!torture_open_connection(&cli)) { + return False; + } + + mem_ctx = talloc_init("torture_sfileinfo"); + + fnum = create_complex_file(cli, mem_ctx, fname); + cli_close(cli, fnum); + + sfinfo.generic.level = RAW_SFILEINFO_STANDARD; + sfinfo.generic.file.fname = fname; + + sfinfo.standard.in.create_time = 0; + sfinfo.standard.in.access_time = 0; + sfinfo.standard.in.write_time = 0; + + status = smb_raw_setpathinfo(cli->tree, &sfinfo); + printf("%s - %s\n", fname, nt_errstr(status)); + + printf("now try and delete %s\n", fname); + + return True; +} diff --git a/source4/torture/raw/unlink.c b/source4/torture/raw/unlink.c new file mode 100644 index 0000000000..9cae91fe41 --- /dev/null +++ b/source4/torture/raw/unlink.c @@ -0,0 +1,148 @@ +/* + Unix SMB/CIFS implementation. + unlink test suite + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + printf("(%d) Incorrect status %s - should be %s\n", \ + __LINE__, nt_errstr(status), nt_errstr(correct)); \ + ret = False; \ + goto done; \ + }} while (0) + +#define BASEDIR "\\testunlink" + +/* + test unlink ops +*/ +static BOOL test_unlink(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + struct smb_unlink io; + NTSTATUS status; + BOOL ret = True; + const char *fname = BASEDIR "\\test.txt"; + + if (cli_deltree(cli, BASEDIR) == -1 || + !cli_mkdir(cli, BASEDIR)) { + printf("Unable to setup %s - %s\n", BASEDIR, cli_errstr(cli)); + return False; + } + + printf("Trying non-existant file\n"); + io.in.pattern = fname; + io.in.attrib = 0; + status = smb_raw_unlink(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + + cli_close(cli, cli_open(cli, fname, O_RDWR|O_CREAT, DENY_NONE)); + + io.in.pattern = fname; + io.in.attrib = 0; + status = smb_raw_unlink(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + printf("Trying a hidden file\n"); + cli_close(cli, cli_open(cli, fname, O_RDWR|O_CREAT, DENY_NONE)); + torture_set_file_attribute(cli->tree, fname, FILE_ATTRIBUTE_HIDDEN); + + io.in.pattern = fname; + io.in.attrib = 0; + status = smb_raw_unlink(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_NO_SUCH_FILE); + + io.in.pattern = fname; + io.in.attrib = FILE_ATTRIBUTE_HIDDEN; + status = smb_raw_unlink(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + printf("Trying a directory\n"); + io.in.pattern = BASEDIR; + io.in.attrib = 0; + status = smb_raw_unlink(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_IS_A_DIRECTORY); + + io.in.pattern = BASEDIR; + io.in.attrib = FILE_ATTRIBUTE_DIRECTORY; + status = smb_raw_unlink(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_IS_A_DIRECTORY); + + printf("Trying a bad path\n"); + io.in.pattern = ".."; + io.in.attrib = 0; + status = smb_raw_unlink(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD); + + printf("Trying wildcards\n"); + cli_close(cli, cli_open(cli, fname, O_RDWR|O_CREAT, DENY_NONE)); + io.in.pattern = BASEDIR "\\t*.t"; + io.in.attrib = 0; + status = smb_raw_unlink(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_NO_SUCH_FILE); + + io.in.pattern = BASEDIR "\\*"; + io.in.attrib = FILE_ATTRIBUTE_DIRECTORY; + status = smb_raw_unlink(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_INVALID); + + io.in.pattern = BASEDIR "\\*.dat"; + io.in.attrib = FILE_ATTRIBUTE_DIRECTORY; + status = smb_raw_unlink(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_NO_SUCH_FILE); + + io.in.pattern = BASEDIR "\\*.tx?"; + io.in.attrib = 0; + status = smb_raw_unlink(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb_raw_unlink(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_NO_SUCH_FILE); + + +done: + smb_raw_exit(cli->session); + cli_deltree(cli, BASEDIR); + return ret; +} + + +/* + basic testing of unlink calls +*/ +BOOL torture_raw_unlink(int dummy) +{ + struct cli_state *cli; + BOOL ret = True; + TALLOC_CTX *mem_ctx; + + if (!torture_open_connection(&cli)) { + return False; + } + + mem_ctx = talloc_init("torture_raw_unlink"); + + if (!test_unlink(cli, mem_ctx)) { + ret = False; + } + + torture_close_connection(cli); + talloc_destroy(mem_ctx); + return ret; +} diff --git a/source4/torture/raw/write.c b/source4/torture/raw/write.c new file mode 100644 index 0000000000..117b322530 --- /dev/null +++ b/source4/torture/raw/write.c @@ -0,0 +1,702 @@ +/* + Unix SMB/CIFS implementation. + test suite for various write operations + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + printf("(%d) Incorrect status %s - should be %s\n", \ + __LINE__, nt_errstr(status), nt_errstr(correct)); \ + ret = False; \ + goto done; \ + }} while (0) + +#define CHECK_VALUE(v, correct) do { \ + if ((v) != (correct)) { \ + printf("(%d) Incorrect value %s=%d - should be %d\n", \ + __LINE__, #v, v, correct); \ + ret = False; \ + goto done; \ + }} while (0) + +#define CHECK_BUFFER(buf, seed, len) do { \ + if (!check_buffer(buf, seed, len, __LINE__)) { \ + ret = False; \ + goto done; \ + }} while (0) + +#define CHECK_ALL_INFO(v, field) do { \ + finfo.all_info.level = RAW_FILEINFO_ALL_INFO; \ + finfo.all_info.in.fname = fname; \ + status = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo); \ + CHECK_STATUS(status, NT_STATUS_OK); \ + if ((v) != finfo.all_info.out.field) { \ + printf("(%d) wrong value for field %s %.0f - %.0f\n", \ + __LINE__, #field, (double)v, (double)finfo.all_info.out.field); \ + dump_all_info(mem_ctx, &finfo); \ + ret = False; \ + }} while (0) + + +#define BASEDIR "\\testwrite" + + +/* + setup a random buffer based on a seed +*/ +static void setup_buffer(char *buf, unsigned seed, int len) +{ + int i; + srandom(seed); + for (i=0;itree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.write.out.nwritten, 0); + + setup_buffer(buf, seed, maxsize); + + printf("Trying small write\n"); + io.write.in.count = 9; + io.write.in.offset = 4; + io.write.in.data = buf; + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.write.out.nwritten, io.write.in.count); + + memset(buf, 0, maxsize); + if (cli_read(cli, fnum, buf, 0, 13) != 13) { + printf("read failed at %d\n", __LINE__); + ret = False; + goto done; + } + CHECK_BUFFER(buf+4, seed, 9); + CHECK_VALUE(IVAL(buf,0), 0); + + setup_buffer(buf, seed, maxsize); + + printf("Trying large write\n"); + io.write.in.count = 4000; + io.write.in.offset = 0; + io.write.in.data = buf; + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.write.out.nwritten, 4000); + + memset(buf, 0, maxsize); + if (cli_read(cli, fnum, buf, 0, 4000) != 4000) { + printf("read failed at %d\n", __LINE__); + ret = False; + goto done; + } + CHECK_BUFFER(buf, seed, 4000); + + printf("Trying bad fnum\n"); + io.write.in.fnum = fnum+1; + io.write.in.count = 4000; + io.write.in.offset = 0; + io.write.in.data = buf; + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + + printf("Setting file as sparse\n"); + status = torture_set_sparse(cli->tree, fnum); + CHECK_STATUS(status, NT_STATUS_OK); + + printf("Trying 2^32 offset\n"); + setup_buffer(buf, seed, maxsize); + io.write.in.fnum = fnum; + io.write.in.count = 4000; + io.write.in.offset = 0xFFFFFFFF - 2000; + io.write.in.data = buf; + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.write.out.nwritten, 4000); + CHECK_ALL_INFO(io.write.in.count + (SMB_BIG_UINT)io.write.in.offset, size); + + memset(buf, 0, maxsize); + if (cli_read(cli, fnum, buf, io.write.in.offset, 4000) != 4000) { + printf("read failed at %d\n", __LINE__); + ret = False; + goto done; + } + CHECK_BUFFER(buf, seed, 4000); + +done: + smb_raw_exit(cli->session); + cli_deltree(cli, BASEDIR); + return ret; +} + + +/* + test writex ops +*/ +static BOOL test_writex(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + union smb_write io; + NTSTATUS status; + BOOL ret = True; + int fnum; + char *buf; + const int maxsize = 90000; + const char *fname = BASEDIR "\\test.txt"; + unsigned seed = time(NULL); + union smb_fileinfo finfo; + + buf = talloc_zero(mem_ctx, maxsize); + + if (cli_deltree(cli, BASEDIR) == -1 || + !cli_mkdir(cli, BASEDIR)) { + printf("Unable to setup %s - %s\n", BASEDIR, cli_errstr(cli)); + return False; + } + + printf("Testing RAW_WRITE_WRITEX\n"); + io.generic.level = RAW_WRITE_WRITEX; + + fnum = cli_open(cli, fname, O_RDWR|O_CREAT, DENY_NONE); + if (fnum == -1) { + printf("Failed to create %s - %s\n", fname, cli_errstr(cli)); + ret = False; + goto done; + } + + printf("Trying zero write\n"); + io.writex.in.fnum = fnum; + io.writex.in.offset = 0; + io.writex.in.wmode = 0; + io.writex.in.remaining = 0; + io.writex.in.count = 0; + io.writex.in.data = buf; + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.writex.out.nwritten, 0); + + setup_buffer(buf, seed, maxsize); + + printf("Trying small write\n"); + io.writex.in.count = 9; + io.writex.in.offset = 4; + io.writex.in.data = buf; + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.writex.out.nwritten, io.writex.in.count); + + memset(buf, 0, maxsize); + if (cli_read(cli, fnum, buf, 0, 13) != 13) { + printf("read failed at %d\n", __LINE__); + ret = False; + goto done; + } + CHECK_BUFFER(buf+4, seed, 9); + CHECK_VALUE(IVAL(buf,0), 0); + + setup_buffer(buf, seed, maxsize); + + printf("Trying large write\n"); + io.writex.in.count = 4000; + io.writex.in.offset = 0; + io.writex.in.data = buf; + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.writex.out.nwritten, 4000); + + memset(buf, 0, maxsize); + if (cli_read(cli, fnum, buf, 0, 4000) != 4000) { + printf("read failed at %d\n", __LINE__); + ret = False; + goto done; + } + CHECK_BUFFER(buf, seed, 4000); + + printf("Trying bad fnum\n"); + io.writex.in.fnum = fnum+1; + io.writex.in.count = 4000; + io.writex.in.offset = 0; + io.writex.in.data = buf; + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + + printf("Testing wmode\n"); + io.writex.in.fnum = fnum; + io.writex.in.count = 1; + io.writex.in.offset = 0; + io.writex.in.wmode = 1; + io.writex.in.data = buf; + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.writex.out.nwritten, io.writex.in.count); + + io.writex.in.wmode = 2; + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.writex.out.nwritten, io.writex.in.count); + + + printf("Trying locked region\n"); + cli->session->pid++; + if (!cli_lock(cli, fnum, 3, 1, 0, WRITE_LOCK)) { + printf("Failed to lock file at %d\n", __LINE__); + ret = False; + goto done; + } + cli->session->pid--; + io.writex.in.wmode = 0; + io.writex.in.count = 4; + io.writex.in.offset = 0; + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT); + + printf("Setting file as sparse\n"); + status = torture_set_sparse(cli->tree, fnum); + CHECK_STATUS(status, NT_STATUS_OK); + + printf("Trying 2^32 offset\n"); + setup_buffer(buf, seed, maxsize); + io.writex.in.fnum = fnum; + io.writex.in.count = 4000; + io.writex.in.offset = 0xFFFFFFFF - 2000; + io.writex.in.data = buf; + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.writex.out.nwritten, 4000); + CHECK_ALL_INFO(io.writex.in.count + (SMB_BIG_UINT)io.writex.in.offset, size); + + memset(buf, 0, maxsize); + if (cli_read(cli, fnum, buf, io.writex.in.offset, 4000) != 4000) { + printf("read failed at %d\n", __LINE__); + ret = False; + goto done; + } + CHECK_BUFFER(buf, seed, 4000); + + printf("Trying 2^43 offset\n"); + setup_buffer(buf, seed+1, maxsize); + io.writex.in.fnum = fnum; + io.writex.in.count = 4000; + io.writex.in.offset = ((SMB_BIG_UINT)1) << 43; + io.writex.in.data = buf; + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.writex.out.nwritten, 4000); + CHECK_ALL_INFO(io.writex.in.count + (SMB_BIG_UINT)io.writex.in.offset, size); + + memset(buf, 0, maxsize); + if (cli_read(cli, fnum, buf, io.writex.in.offset, 4000) != 4000) { + printf("read failed at %d\n", __LINE__); + ret = False; + goto done; + } + CHECK_BUFFER(buf, seed+1, 4000); + + + setup_buffer(buf, seed, maxsize); + +done: + smb_raw_exit(cli->session); + cli_deltree(cli, BASEDIR); + return ret; +} + + +/* + test write unlock ops +*/ +static BOOL test_writeunlock(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + union smb_write io; + NTSTATUS status; + BOOL ret = True; + int fnum; + char *buf; + const int maxsize = 90000; + const char *fname = BASEDIR "\\test.txt"; + unsigned seed = time(NULL); + union smb_fileinfo finfo; + + buf = talloc_zero(mem_ctx, maxsize); + + if (cli_deltree(cli, BASEDIR) == -1 || + !cli_mkdir(cli, BASEDIR)) { + printf("Unable to setup %s - %s\n", BASEDIR, cli_errstr(cli)); + return False; + } + + printf("Testing RAW_WRITE_WRITEUNLOCK\n"); + io.generic.level = RAW_WRITE_WRITEUNLOCK; + + fnum = cli_open(cli, fname, O_RDWR|O_CREAT, DENY_NONE); + if (fnum == -1) { + printf("Failed to create %s - %s\n", fname, cli_errstr(cli)); + ret = False; + goto done; + } + + printf("Trying zero write\n"); + io.writeunlock.in.fnum = fnum; + io.writeunlock.in.count = 0; + io.writeunlock.in.offset = 0; + io.writeunlock.in.remaining = 0; + io.writeunlock.in.data = buf; + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.writeunlock.out.nwritten, io.writeunlock.in.count); + + setup_buffer(buf, seed, maxsize); + + printf("Trying small write\n"); + io.writeunlock.in.count = 9; + io.writeunlock.in.offset = 4; + io.writeunlock.in.data = buf; + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + if (cli_read(cli, fnum, buf, 0, 13) != 13) { + printf("read failed at %d\n", __LINE__); + ret = False; + goto done; + } + CHECK_BUFFER(buf+4, seed, 9); + CHECK_VALUE(IVAL(buf,0), 0); + + setup_buffer(buf, seed, maxsize); + cli_lock(cli, fnum, io.writeunlock.in.offset, io.writeunlock.in.count, + 0, WRITE_LOCK); + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.writeunlock.out.nwritten, io.writeunlock.in.count); + + memset(buf, 0, maxsize); + if (cli_read(cli, fnum, buf, 0, 13) != 13) { + printf("read failed at %d\n", __LINE__); + ret = False; + goto done; + } + CHECK_BUFFER(buf+4, seed, 9); + CHECK_VALUE(IVAL(buf,0), 0); + + setup_buffer(buf, seed, maxsize); + + printf("Trying large write\n"); + io.writeunlock.in.count = 4000; + io.writeunlock.in.offset = 0; + io.writeunlock.in.data = buf; + cli_lock(cli, fnum, io.writeunlock.in.offset, io.writeunlock.in.count, + 0, WRITE_LOCK); + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.writeunlock.out.nwritten, 4000); + + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); + + memset(buf, 0, maxsize); + if (cli_read(cli, fnum, buf, 0, 4000) != 4000) { + printf("read failed at %d\n", __LINE__); + ret = False; + goto done; + } + CHECK_BUFFER(buf, seed, 4000); + + printf("Trying bad fnum\n"); + io.writeunlock.in.fnum = fnum+1; + io.writeunlock.in.count = 4000; + io.writeunlock.in.offset = 0; + io.writeunlock.in.data = buf; + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + + printf("Setting file as sparse\n"); + status = torture_set_sparse(cli->tree, fnum); + CHECK_STATUS(status, NT_STATUS_OK); + + printf("Trying 2^32 offset\n"); + setup_buffer(buf, seed, maxsize); + io.writeunlock.in.fnum = fnum; + io.writeunlock.in.count = 4000; + io.writeunlock.in.offset = 0xFFFFFFFF - 2000; + io.writeunlock.in.data = buf; + cli_lock(cli, fnum, io.writeunlock.in.offset, io.writeunlock.in.count, + 0, WRITE_LOCK); + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.writeunlock.out.nwritten, 4000); + CHECK_ALL_INFO(io.writeunlock.in.count + (SMB_BIG_UINT)io.writeunlock.in.offset, size); + + memset(buf, 0, maxsize); + if (cli_read(cli, fnum, buf, io.writeunlock.in.offset, 4000) != 4000) { + printf("read failed at %d\n", __LINE__); + ret = False; + goto done; + } + CHECK_BUFFER(buf, seed, 4000); + +done: + smb_raw_exit(cli->session); + cli_deltree(cli, BASEDIR); + return ret; +} + + +/* + test write close ops +*/ +static BOOL test_writeclose(struct cli_state *cli, TALLOC_CTX *mem_ctx) +{ + union smb_write io; + NTSTATUS status; + BOOL ret = True; + int fnum; + char *buf; + const int maxsize = 90000; + const char *fname = BASEDIR "\\test.txt"; + unsigned seed = time(NULL); + union smb_fileinfo finfo; + + buf = talloc_zero(mem_ctx, maxsize); + + if (cli_deltree(cli, BASEDIR) == -1 || + !cli_mkdir(cli, BASEDIR)) { + printf("Unable to setup %s - %s\n", BASEDIR, cli_errstr(cli)); + return False; + } + + printf("Testing RAW_WRITE_WRITECLOSE\n"); + io.generic.level = RAW_WRITE_WRITECLOSE; + + fnum = cli_open(cli, fname, O_RDWR|O_CREAT, DENY_NONE); + if (fnum == -1) { + printf("Failed to create %s - %s\n", fname, cli_errstr(cli)); + ret = False; + goto done; + } + + printf("Trying zero write\n"); + io.writeclose.in.fnum = fnum; + io.writeclose.in.count = 0; + io.writeclose.in.offset = 0; + io.writeclose.in.mtime = 0; + io.writeclose.in.data = buf; + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.writeclose.out.nwritten, io.writeclose.in.count); + + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.writeclose.out.nwritten, io.writeclose.in.count); + + setup_buffer(buf, seed, maxsize); + + printf("Trying small write\n"); + io.writeclose.in.count = 9; + io.writeclose.in.offset = 4; + io.writeclose.in.data = buf; + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + + fnum = cli_open(cli, fname, O_RDWR, DENY_NONE); + io.writeclose.in.fnum = fnum; + + if (cli_read(cli, fnum, buf, 0, 13) != 13) { + printf("read failed at %d\n", __LINE__); + ret = False; + goto done; + } + CHECK_BUFFER(buf+4, seed, 9); + CHECK_VALUE(IVAL(buf,0), 0); + + setup_buffer(buf, seed, maxsize); + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.writeclose.out.nwritten, io.writeclose.in.count); + + fnum = cli_open(cli, fname, O_RDWR, DENY_NONE); + io.writeclose.in.fnum = fnum; + + memset(buf, 0, maxsize); + if (cli_read(cli, fnum, buf, 0, 13) != 13) { + printf("read failed at %d\n", __LINE__); + ret = False; + goto done; + } + CHECK_BUFFER(buf+4, seed, 9); + CHECK_VALUE(IVAL(buf,0), 0); + + setup_buffer(buf, seed, maxsize); + + printf("Trying large write\n"); + io.writeclose.in.count = 4000; + io.writeclose.in.offset = 0; + io.writeclose.in.data = buf; + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.writeclose.out.nwritten, 4000); + + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + + fnum = cli_open(cli, fname, O_RDWR, DENY_NONE); + io.writeclose.in.fnum = fnum; + + memset(buf, 0, maxsize); + if (cli_read(cli, fnum, buf, 0, 4000) != 4000) { + printf("read failed at %d\n", __LINE__); + ret = False; + goto done; + } + CHECK_BUFFER(buf, seed, 4000); + + printf("Trying bad fnum\n"); + io.writeclose.in.fnum = fnum+1; + io.writeclose.in.count = 4000; + io.writeclose.in.offset = 0; + io.writeclose.in.data = buf; + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE); + + printf("Setting file as sparse\n"); + status = torture_set_sparse(cli->tree, fnum); + CHECK_STATUS(status, NT_STATUS_OK); + + printf("Trying 2^32 offset\n"); + setup_buffer(buf, seed, maxsize); + io.writeclose.in.fnum = fnum; + io.writeclose.in.count = 4000; + io.writeclose.in.offset = 0xFFFFFFFF - 2000; + io.writeclose.in.data = buf; + status = smb_raw_write(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(io.writeclose.out.nwritten, 4000); + CHECK_ALL_INFO(io.writeclose.in.count + (SMB_BIG_UINT)io.writeclose.in.offset, size); + + fnum = cli_open(cli, fname, O_RDWR, DENY_NONE); + io.writeclose.in.fnum = fnum; + + memset(buf, 0, maxsize); + if (cli_read(cli, fnum, buf, io.writeclose.in.offset, 4000) != 4000) { + printf("read failed at %d\n", __LINE__); + ret = False; + goto done; + } + CHECK_BUFFER(buf, seed, 4000); + +done: + smb_raw_exit(cli->session); + cli_deltree(cli, BASEDIR); + return ret; +} + + +/* + basic testing of write calls +*/ +BOOL torture_raw_write(int dummy) +{ + struct cli_state *cli; + BOOL ret = True; + TALLOC_CTX *mem_ctx; + + if (!torture_open_connection(&cli)) { + return False; + } + + mem_ctx = talloc_init("torture_raw_write"); + + if (!test_write(cli, mem_ctx)) { + ret = False; + } + + if (!test_writeunlock(cli, mem_ctx)) { + ret = False; + } + + if (!test_writeclose(cli, mem_ctx)) { + ret = False; + } + + if (!test_writex(cli, mem_ctx)) { + ret = False; + } + + torture_close_connection(cli); + talloc_destroy(mem_ctx); + return ret; +} diff --git a/source4/torture/rpctorture.c b/source4/torture/rpctorture.c new file mode 100644 index 0000000000..04656e71d1 --- /dev/null +++ b/source4/torture/rpctorture.c @@ -0,0 +1,526 @@ +/* + 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 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 [-d debuglevel] [-l log] ", + pname); + + fprintf(out_hnd, "\nVersion %s\n",VERSION); + 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); + + 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': + lp_set_cmdline("socket options", optarg); + break; + + case 'S': + { + pstrcpy(cli_info.dest_host,optarg); + strupper(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': + lp_set_cmdline("netbios name", 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); + } + + fstrcpy(cli_info.myhostname, lp_netbios_name()); + + DEBUG(3,("%s client started (version %s)\n",timestring(False),VERSION)); + + if (*smb_cli->domain == 0) + { + pstrcpy(smb_cli->domain,lp_workgroup()); + } + strupper(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(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/source4/torture/samtest.c b/source4/torture/samtest.c new file mode 100644 index 0000000000..832f3ecea5 --- /dev/null +++ b/source4/torture/samtest.c @@ -0,0 +1,451 @@ +/* + Unix SMB/CIFS implementation. + SAM module tester + + Copyright (C) 2002 Jelmer Vernooij + + Parts of the code stolen from vfstest by Simo Sorce and Eric Lorimer + Parts of the code stolen from rpcclient by Tim Potter + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 "samtest.h" + +struct func_entry { + char *name; + int (*fn)(struct tcon_context *conn, const char *path); +}; + +/* List to hold groups of commands */ +static struct cmd_list { + struct cmd_list *prev, *next; + struct cmd_set *cmd_set; +} *cmd_list; + +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 samtest_state *sam, TALLOC_CTX *mem_ctx, + int argc, char **argv) +{ + if (argc != 2) { + printf("Usage: %s \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 samtest_state *st, 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("%20s\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 samtest_state *st, TALLOC_CTX *mem_ctx, int argc, 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 samtest_state *st, TALLOC_CTX *mem_ctx, int argc, char **argv) +{ + /* Cleanup */ + talloc_destroy(mem_ctx); + + exit(0); + return NT_STATUS_OK; /* NOTREACHED */ +} + +static struct cmd_set samtest_commands[] = { + + { "GENERAL OPTIONS" }, + + { "help", cmd_help, "Get help on commands", "" }, + { "?", cmd_help, "Get help on commands", "" }, + { "conf", cmd_conf, "Load smb configuration file", "conf " }, + { "debuglevel", cmd_debuglevel, "Set debug level", "" }, + { "exit", cmd_quit, "Exit program", "" }, + { "quit", cmd_quit, "Exit program", "" }, + + { NULL } +}; + +static struct cmd_set separator_command[] = { + { "---------------", NULL, "----------------------" }, + { NULL } +}; + + +/*extern struct cmd_set sam_commands[];*/ +extern struct cmd_set sam_general_commands[]; +extern struct cmd_set sam_domain_commands[]; +extern struct cmd_set sam_account_commands[]; +extern struct cmd_set sam_group_commands[]; +static struct cmd_set *samtest_command_list[] = { + samtest_commands, + sam_general_commands, + sam_domain_commands, + sam_account_commands, + sam_group_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 samtest_state *st, struct cmd_set *cmd_entry, char *cmd) +{ + char *p = cmd, **argv = NULL; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + TALLOC_CTX *mem_ctx = NULL; + pstring buf; + 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(st, mem_ctx, argc, 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 samtest_state *st, char *cmd) +{ + struct cmd_list *temp_list; + BOOL found = False; + pstring buf; + 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(st, 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; +} + +void exit_server(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; +} + +BOOL reload_services(BOOL test) +{ + return True; +} + +/* Main function */ + +int main(int argc, char *argv[]) +{ + BOOL interactive = True; + int opt; + static char *cmdstr = ""; + static char *opt_logfile=NULL; + static char *config_file = dyn_CONFIGFILE; + pstring logfile; + struct cmd_set **cmd_set; + struct samtest_state st; + + /* 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 + { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_debug }, + {"command", 'e', POPT_ARG_STRING, &cmdstr, 'e', "Execute semicolon seperated cmds"}, + {"logfile", 'l', POPT_ARG_STRING, &opt_logfile, 'l', "Logfile to use instead of stdout"}, + {"configfile", 'c', POPT_ARG_STRING, &config_file, 0,"use different configuration file",NULL}, + { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_version}, + { 0, 0, 0, 0} + }; + + ZERO_STRUCT(st); + + st.token = get_system_token(); + + setlinebuf(stdout); + + DEBUGLEVEL = 1; + + pc = poptGetContext("samtest", argc, (const char **) argv, + long_options, 0); + + while((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case 'l': + slprintf(logfile, sizeof(logfile) - 1, "%s.client", + opt_logfile); + lp_set_logfile(logfile); + interactive = False; + break; + } + } + + if (!lp_load(config_file,True,False,False)) { + fprintf(stderr, "Can't load %s - run testparm to debug it\n", config_file); + exit(1); + } + + poptFreeContext(pc); + + /* the following functions are part of the Samba debugging + facilities. See lib/debug.c */ + setup_logging("samtest", interactive); + if (!interactive) + reopen_logs(); + + /* Load command lists */ + + cmd_set = samtest_command_list; + + while(*cmd_set) { + add_command_set(*cmd_set); + add_command_set(separator_command); + cmd_set++; + } + + /* Do anything specified with -c */ + if (cmdstr[0]) { + char *cmd; + char *p = cmdstr; + + while((cmd=next_command(&p)) != NULL) { + process_cmd(&st, cmd); + } + + return 0; + } + + /* Loop around accepting commands */ + + while(1) { + pstring prompt; + char *line; + + slprintf(prompt, sizeof(prompt) - 1, "samtest $> "); + + line = smb_readline(prompt, NULL, NULL); + + if (line == NULL) + break; + + if (line[0] != '\n') + process_cmd(&st, line); + } + + return 0; +} diff --git a/source4/torture/samtest.h b/source4/torture/samtest.h new file mode 100644 index 0000000000..a136ab191e --- /dev/null +++ b/source4/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/source4/torture/scanner.c b/source4/torture/scanner.c new file mode 100644 index 0000000000..0a92db9a4b --- /dev/null +++ b/source4/torture/scanner.c @@ -0,0 +1,514 @@ +/* + 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. +*/ + +#include "includes.h" + +#define VERBOSE 0 +#define OP_MIN 0 +#define OP_MAX 100 + +/**************************************************************************** +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) +{ + NTSTATUS status; + struct smb_trans2 t2; + uint16 setup = op; + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_init("try_trans2"); + + t2.in.max_param = 1024; + t2.in.max_data = 0x8000; + t2.in.max_setup = 10; + t2.in.flags = 0; + t2.in.timeout = 0; + t2.in.setup_count = 1; + t2.in.setup = &setup; + t2.in.params.data = param; + t2.in.params.length = param_len; + t2.in.data.data = data; + t2.in.data.length = data_len; + + status = smb_raw_trans2(cli->tree, mem_ctx, &t2); + + *rparam_len = t2.out.params.length; + *rdata_len = t2.out.data.length; + + talloc_destroy(mem_ctx); + + return status; +} + + +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 whether a trans2 opnum exists at all +****************************************************************************/ +static BOOL trans2_op_exists(struct cli_state *cli, int op) +{ + int data_len = 0; + int param_len = 0; + int rparam_len, rdata_len; + pstring param, data; + NTSTATUS status1, status2; + + memset(data, 0, sizeof(data)); + data_len = 4; + + /* try with a info level only */ + param_len = sizeof(param); + data_len = sizeof(data); + + memset(param, 0xFF, sizeof(param)); + memset(data, 0xFF, sizeof(data)); + + status1 = try_trans2(cli, 0xFFFF, param, data, param_len, data_len, + &rparam_len, &rdata_len); + + status2 = try_trans2(cli, op, param, data, param_len, data_len, + &rparam_len, &rdata_len); + + if (NT_STATUS_EQUAL(status1, status2)) return False; + + printf("Found op %d (status=%s)\n", op, nt_errstr(status2)); + + return True; +} + +/**************************************************************************** +check for existance of a trans2 call +****************************************************************************/ +static BOOL scan_trans2(struct cli_state *cli, int op, int level, + int fnum, int dnum, int qfnum, const char *fname) +{ + int data_len = 0; + int param_len = 0; + int rparam_len, rdata_len; + 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 quota file descriptor */ + param_len = 6; + SSVAL(param, 0, qfnum); + SSVAL(param, 2, level); + SSVAL(param, 4, 0); + status = try_trans2_len(cli, "qfnum", op, level, param, data, param_len, &data_len, + &rparam_len, &rdata_len); + if (NT_STATUS_IS_OK(status)) 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 += push_string(NULL, ¶m[6], fname, sizeof(pstring)-7, STR_TERMINATE|STR_UNICODE); + + status = try_trans2_len(cli, "fname", op, level, param, data, param_len, &data_len, + &rparam_len, &rdata_len); + if (NT_STATUS_IS_OK(status)) return True; + + /* try with a new file name */ + param_len = 6; + SSVAL(param, 0, level); + SSVAL(param, 2, 0); + SSVAL(param, 4, 0); + param_len += push_string(NULL, ¶m[6], "\\newfile.dat", sizeof(pstring)-7, STR_TERMINATE|STR_UNICODE); + + 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 += push_string(NULL, ¶m[2], "\\testdir", sizeof(pstring)-3, STR_TERMINATE|STR_UNICODE); + + 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, qfnum; + + 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); + if (fnum == -1) { + printf("file open failed - %s\n", cli_errstr(cli)); + } + dnum = cli_nt_create_full(cli, "\\", + 0, GENERIC_RIGHTS_FILE_READ, FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE, + NTCREATEX_DISP_OPEN, + NTCREATEX_OPTIONS_DIRECTORY, 0); + if (dnum == -1) { + printf("directory open failed - %s\n", cli_errstr(cli)); + } + qfnum = cli_nt_create_full(cli, "\\$Extend\\$Quota:$Q:$INDEX_ALLOCATION", + NTCREATEX_FLAGS_EXTENDED, + SEC_RIGHTS_MAXIMUM_ALLOWED, + 0, + NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE, + NTCREATEX_DISP_OPEN, + 0, 0); + if (qfnum == -1) { + printf("quota open failed - %s\n", cli_errstr(cli)); + } + + for (op=OP_MIN; op<=OP_MAX; op++) { + + if (!trans2_op_exists(cli, op)) { + continue; + } + + for (level = 0; level <= 50; level++) { + scan_trans2(cli, op, level, fnum, dnum, qfnum, fname); + } + + for (level = 0x100; level <= 0x130; level++) { + scan_trans2(cli, op, level, fnum, dnum, qfnum, fname); + } + + for (level = 1000; level < 1050; level++) { + scan_trans2(cli, op, level, fnum, dnum, qfnum, fname); + } + } + + 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 existence 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) +{ + struct smb_nttrans parms; + DATA_BLOB ntparam_blob, ntdata_blob; + TALLOC_CTX *mem_ctx; + NTSTATUS status; + + mem_ctx = talloc_init("try_nttrans"); + + ntparam_blob.length = param_len; + ntparam_blob.data = param; + ntdata_blob.length = data_len; + ntdata_blob.data = data; + + parms.in.max_param = 1024; + parms.in.max_data = 1024; + parms.in.max_setup = 0; + parms.in.setup_count = 0; + parms.in.function = op; + parms.in.params = ntparam_blob; + parms.in.data = ntdata_blob; + + status = smb_raw_nttrans(cli->tree, mem_ctx, &parms); + + if (NT_STATUS_IS_ERR(status)) { + DEBUG(1,("Failed to send NT_TRANS\n")); + talloc_destroy(mem_ctx); + return status; + } + *rparam_len = parms.out.params.length; + *rdata_len = parms.out.data.length; + + talloc_destroy(mem_ctx); + + return status; +} + + +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 += push_string(NULL, ¶m[6], fname, -1, STR_TERMINATE | STR_UNICODE); + + status = try_nttrans_len(cli, "fname", op, level, param, data, param_len, &data_len, + &rparam_len, &rdata_len); + if (NT_STATUS_IS_OK(status)) return True; + + /* try with a new file name */ + param_len = 6; + SSVAL(param, 0, level); + SSVAL(param, 2, 0); + SSVAL(param, 4, 0); + param_len += push_string(NULL, ¶m[6], "\\newfile.dat", -1, STR_TERMINATE | STR_UNICODE); + + 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 += push_string(NULL, ¶m[2], "\\testdir", -1, STR_TERMINATE | STR_UNICODE); + + 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/source4/torture/search.c b/source4/torture/search.c new file mode 100644 index 0000000000..cf9e28b0fd --- /dev/null +++ b/source4/torture/search.c @@ -0,0 +1,325 @@ +/* + Unix SMB/CIFS implementation. + RAW_SEARCH_* individual test suite + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + + +/* + callback function for single_search +*/ +static BOOL single_search_callback(void *private, union smb_search_data *file) +{ + union smb_search_data *data = private; + + *data = *file; + + return True; +} + +/* + do a single file (non-wildcard) search +*/ +static NTSTATUS single_search(struct cli_state *cli, + TALLOC_CTX *mem_ctx, + const char *pattern, + enum search_level level, + union smb_search_data *data) +{ + union smb_search_first io; + NTSTATUS status; + + io.generic.level = level; + if (level == RAW_SEARCH_SEARCH) { + io.search_first.in.max_count = 1; + io.search_first.in.search_attrib = 0; + io.search_first.in.pattern = pattern; + } else { + io.t2ffirst.in.search_attrib = 0; + io.t2ffirst.in.max_count = 1; + io.t2ffirst.in.flags = FLAG_TRANS2_FIND_CLOSE; + io.t2ffirst.in.storage_type = 0; + io.t2ffirst.in.pattern = pattern; + } + + status = smb_raw_search_first(cli->tree, mem_ctx, + &io, (void *)data, single_search_callback); + + return status; +} + + +static struct { + const char *name; + enum search_level level; + NTSTATUS status; + union smb_search_data data; +} levels[] = { + {"SEARCH", RAW_SEARCH_SEARCH, }, + {"STANDARD", RAW_SEARCH_STANDARD, }, + {"EA_SIZE", RAW_SEARCH_EA_SIZE, }, + {"DIRECTORY_INFO", RAW_SEARCH_DIRECTORY_INFO, }, + {"FULL_DIRECTORY_INFO", RAW_SEARCH_FULL_DIRECTORY_INFO, }, + {"NAME_INFO", RAW_SEARCH_NAME_INFO, }, + {"BOTH_DIRECTORY_INFO", RAW_SEARCH_BOTH_DIRECTORY_INFO, }, + {"LEVEL_261", RAW_SEARCH_261, }, + {"LEVEL_262", RAW_SEARCH_262, } +}; + +/* find a level in the table by name */ +static union smb_search_data *find(const char *name) +{ + int i; + for (i=0;levels[i].name;i++) { + if (NT_STATUS_IS_OK(levels[i].status) && + strcmp(levels[i].name, name) == 0) { + return &levels[i].data; + } + } + return NULL; +} + +/* + basic testing of all RAW_SEARCH_* calls +*/ +BOOL torture_search(int dummy) +{ + struct cli_state *cli; + BOOL ret = True; + TALLOC_CTX *mem_ctx; + int fnum; + const char *fname = "\\torture_search.txt"; + NTSTATUS status; + int i; + union smb_fileinfo all_info, alt_info, name_info; + union smb_search_data *s; + + if (!torture_open_connection(&cli)) { + return False; + } + + mem_ctx = talloc_init("torture_search"); + + fnum = create_complex_file(cli, mem_ctx, fname); + if (fnum == -1) { + printf("ERROR: open of %s failed (%s)\n", fname, cli_errstr(cli)); + ret = False; + goto done; + } + + /* call all the levels */ + for (i=0;levels[i].name;i++) { + levels[i].status = single_search(cli, mem_ctx, fname, + levels[i].level, &levels[i].data); + if (!NT_STATUS_IS_OK(levels[i].status)) { + printf("search level %s(%d) failed - %s\n", + levels[i].name, (int)levels[i].level, + nt_errstr(levels[i].status)); + ret = False; + } + } + + /* get the all_info file into to check against */ + all_info.generic.level = RAW_FILEINFO_ALL_INFO; + all_info.generic.in.fname = fname; + status = smb_raw_pathinfo(cli->tree, mem_ctx, &all_info); + if (!NT_STATUS_IS_OK(status)) { + printf("RAW_FILEINFO_ALL_INFO failed - %s\n", nt_errstr(status)); + ret = False; + goto done; + } + + alt_info.generic.level = RAW_FILEINFO_ALT_NAME_INFO; + alt_info.generic.in.fname = fname; + status = smb_raw_pathinfo(cli->tree, mem_ctx, &alt_info); + if (!NT_STATUS_IS_OK(status)) { + printf("RAW_FILEINFO_ALT_NAME_INFO failed - %s\n", nt_errstr(status)); + ret = False; + goto done; + } + + name_info.generic.level = RAW_FILEINFO_NAME_INFO; + name_info.generic.in.fname = fname; + status = smb_raw_pathinfo(cli->tree, mem_ctx, &name_info); + if (!NT_STATUS_IS_OK(status)) { + printf("RAW_FILEINFO_NAME_INFO failed - %s\n", nt_errstr(status)); + ret = False; + goto done; + } + +#define CHECK_VAL(name, sname1, field1, v, sname2, field2) do { \ + s = find(name); \ + if (s) { \ + if (s->sname1.field1 != v.sname2.out.field2) { \ + printf("(%d) %s/%s [%d] != %s/%s [%d]\n", \ + __LINE__, \ + #sname1, #field1, (int)s->sname1.field1, \ + #sname2, #field2, (int)v.sname2.out.field2); \ + ret = False; \ + } \ + }} while (0) + +#define CHECK_TIME(name, sname1, field1, v, sname2, field2) do { \ + s = find(name); \ + if (s) { \ + if (s->sname1.field1 != (~1 & nt_time_to_unix(&v.sname2.out.field2))) { \ + printf("(%d) %s/%s [%s] != %s/%s [%s]\n", \ + __LINE__, \ + #sname1, #field1, time_string(mem_ctx, s->sname1.field1), \ + #sname2, #field2, nt_time_string(mem_ctx, &v.sname2.out.field2)); \ + ret = False; \ + } \ + }} while (0) + +#define CHECK_NTTIME(name, sname1, field1, v, sname2, field2) do { \ + s = find(name); \ + if (s) { \ + if (memcmp(&s->sname1.field1, &v.sname2.out.field2, sizeof(NTTIME))) { \ + printf("(%d) %s/%s [%s] != %s/%s [%s]\n", \ + __LINE__, \ + #sname1, #field1, nt_time_string(mem_ctx, &s->sname1.field1), \ + #sname2, #field2, nt_time_string(mem_ctx, &v.sname2.out.field2)); \ + ret = False; \ + } \ + }} while (0) + +#define CHECK_STR(name, sname1, field1, v, sname2, field2) do { \ + s = find(name); \ + if (s) { \ + if (!s->sname1.field1 || strcmp(s->sname1.field1, v.sname2.out.field2.s)) { \ + printf("(%d) %s/%s [%s] != %s/%s [%s]\n", \ + __LINE__, \ + #sname1, #field1, s->sname1.field1, \ + #sname2, #field2, v.sname2.out.field2.s); \ + ret = False; \ + } \ + }} while (0) + +#define CHECK_WSTR(name, sname1, field1, v, sname2, field2, flags) do { \ + s = find(name); \ + if (s) { \ + if (!s->sname1.field1.s || \ + strcmp(s->sname1.field1.s, v.sname2.out.field2.s) || \ + wire_bad_flags(&s->sname1.field1, flags)) { \ + printf("(%d) %s/%s [%s] != %s/%s [%s]\n", \ + __LINE__, \ + #sname1, #field1, s->sname1.field1.s, \ + #sname2, #field2, v.sname2.out.field2.s); \ + ret = False; \ + } \ + }} while (0) + +#define CHECK_NAME(name, sname1, field1, fname, flags) do { \ + s = find(name); \ + if (s) { \ + if (!s->sname1.field1.s || \ + strcmp(s->sname1.field1.s, fname) || \ + wire_bad_flags(&s->sname1.field1, flags)) { \ + printf("(%d) %s/%s [%s] != %s\n", \ + __LINE__, \ + #sname1, #field1, s->sname1.field1.s, \ + fname); \ + ret = False; \ + } \ + }} while (0) + + /* check that all the results are as expected */ + CHECK_VAL("SEARCH", search, attrib, all_info, all_info, attrib); + CHECK_VAL("STANDARD", standard, attrib, all_info, all_info, attrib); + CHECK_VAL("EA_SIZE", ea_size, attrib, all_info, all_info, attrib); + CHECK_VAL("DIRECTORY_INFO", directory_info, attrib, all_info, all_info, attrib); + CHECK_VAL("FULL_DIRECTORY_INFO", full_directory_info, attrib, all_info, all_info, attrib); + CHECK_VAL("BOTH_DIRECTORY_INFO", both_directory_info, attrib, all_info, all_info, attrib); + CHECK_VAL("LEVEL_261", level_261, attrib, all_info, all_info, attrib); + CHECK_VAL("LEVEL_262", level_262, attrib, all_info, all_info, attrib); + + CHECK_TIME("SEARCH", search, write_time, all_info, all_info, write_time); + CHECK_TIME("STANDARD", standard, write_time, all_info, all_info, write_time); + CHECK_TIME("EA_SIZE", ea_size, write_time, all_info, all_info, write_time); + CHECK_TIME("STANDARD", standard, create_time, all_info, all_info, create_time); + CHECK_TIME("EA_SIZE", ea_size, create_time, all_info, all_info, create_time); + CHECK_TIME("STANDARD", standard, access_time, all_info, all_info, access_time); + CHECK_TIME("EA_SIZE", ea_size, access_time, all_info, all_info, access_time); + + CHECK_NTTIME("DIRECTORY_INFO", directory_info, write_time, all_info, all_info, write_time); + CHECK_NTTIME("FULL_DIRECTORY_INFO", full_directory_info, write_time, all_info, all_info, write_time); + CHECK_NTTIME("BOTH_DIRECTORY_INFO", both_directory_info, write_time, all_info, all_info, write_time); + CHECK_NTTIME("LEVEL_261", level_261, write_time, all_info, all_info, write_time); + CHECK_NTTIME("LEVEL_262", level_262, write_time, all_info, all_info, write_time); + + CHECK_NTTIME("DIRECTORY_INFO", directory_info, create_time, all_info, all_info, create_time); + CHECK_NTTIME("FULL_DIRECTORY_INFO", full_directory_info, create_time, all_info, all_info, create_time); + CHECK_NTTIME("BOTH_DIRECTORY_INFO", both_directory_info, create_time, all_info, all_info, create_time); + CHECK_NTTIME("LEVEL_261", level_261, create_time, all_info, all_info, create_time); + CHECK_NTTIME("LEVEL_262", level_262, create_time, all_info, all_info, create_time); + + CHECK_NTTIME("DIRECTORY_INFO", directory_info, access_time, all_info, all_info, access_time); + CHECK_NTTIME("FULL_DIRECTORY_INFO", full_directory_info, access_time, all_info, all_info, access_time); + CHECK_NTTIME("BOTH_DIRECTORY_INFO", both_directory_info, access_time, all_info, all_info, access_time); + CHECK_NTTIME("LEVEL_261", level_261, access_time, all_info, all_info, access_time); + CHECK_NTTIME("LEVEL_262", level_262, access_time, all_info, all_info, access_time); + + CHECK_NTTIME("DIRECTORY_INFO", directory_info, create_time, all_info, all_info, create_time); + CHECK_NTTIME("FULL_DIRECTORY_INFO", full_directory_info, create_time, all_info, all_info, create_time); + CHECK_NTTIME("BOTH_DIRECTORY_INFO", both_directory_info, create_time, all_info, all_info, create_time); + CHECK_NTTIME("LEVEL_261", level_261, create_time, all_info, all_info, create_time); + CHECK_NTTIME("LEVEL_262", level_262, create_time, all_info, all_info, create_time); + + CHECK_VAL("SEARCH", search, size, all_info, all_info, size); + CHECK_VAL("STANDARD", standard, size, all_info, all_info, size); + CHECK_VAL("EA_SIZE", ea_size, size, all_info, all_info, size); + CHECK_VAL("DIRECTORY_INFO", directory_info, size, all_info, all_info, size); + CHECK_VAL("FULL_DIRECTORY_INFO", full_directory_info, size, all_info, all_info, size); + CHECK_VAL("BOTH_DIRECTORY_INFO", both_directory_info, size, all_info, all_info, size); + CHECK_VAL("LEVEL_261", level_261, size, all_info, all_info, size); + CHECK_VAL("LEVEL_262", level_262, size, all_info, all_info, size); + + CHECK_VAL("STANDARD", standard, alloc_size, all_info, all_info, alloc_size); + CHECK_VAL("EA_SIZE", ea_size, alloc_size, all_info, all_info, alloc_size); + CHECK_VAL("DIRECTORY_INFO", directory_info, alloc_size, all_info, all_info, alloc_size); + CHECK_VAL("FULL_DIRECTORY_INFO", full_directory_info, alloc_size, all_info, all_info, alloc_size); + CHECK_VAL("BOTH_DIRECTORY_INFO", both_directory_info, alloc_size, all_info, all_info, alloc_size); + CHECK_VAL("LEVEL_261", level_261, alloc_size, all_info, all_info, alloc_size); + CHECK_VAL("LEVEL_262", level_262, alloc_size, all_info, all_info, alloc_size); + + CHECK_VAL("EA_SIZE", ea_size, ea_size, all_info, all_info, ea_size); + CHECK_VAL("FULL_DIRECTORY_INFO", full_directory_info, ea_size, all_info, all_info, ea_size); + CHECK_VAL("LEVEL_261", level_261, ea_size, all_info, all_info, ea_size); + CHECK_VAL("LEVEL_262", level_262, ea_size, all_info, all_info, ea_size); + + CHECK_STR("SEARCH", search, name, alt_info, alt_name_info, fname); + CHECK_WSTR("BOTH_DIRECTORY_INFO", both_directory_info, short_name, alt_info, alt_name_info, fname, STR_UNICODE); + + CHECK_NAME("STANDARD", standard, name, fname+1, 0); + CHECK_NAME("EA_SIZE", ea_size, name, fname+1, 0); + CHECK_NAME("DIRECTORY_INFO", directory_info, name, fname+1, STR_TERMINATE_ASCII); + CHECK_NAME("FULL_DIRECTORY_INFO", full_directory_info, name, fname+1, STR_TERMINATE_ASCII); + CHECK_NAME("NAME_INFO", name_info, name, fname+1, STR_TERMINATE_ASCII); + CHECK_NAME("BOTH_DIRECTORY_INFO", both_directory_info, name, fname+1, STR_TERMINATE_ASCII); + CHECK_NAME("LEVEL_261", level_261, name, fname+1, STR_TERMINATE_ASCII); + CHECK_NAME("LEVEL_262", level_262, name, fname+1, STR_TERMINATE_ASCII); + +done: + cli_close(cli, fnum); + cli_unlink(cli, fname); + + torture_close_connection(cli); + talloc_destroy(mem_ctx); + return ret; +} diff --git a/source4/torture/setfileinfo.c b/source4/torture/setfileinfo.c new file mode 100644 index 0000000000..b49ac187ca --- /dev/null +++ b/source4/torture/setfileinfo.c @@ -0,0 +1,471 @@ +/* + Unix SMB/CIFS implementation. + RAW_SFILEINFO_* individual test suite + Copyright (C) Andrew Tridgell 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +/* basic testing of all RAW_SFILEINFO_* calls + for each call we test that it succeeds, and where possible test + for consistency between the calls. +*/ +BOOL torture_sfileinfo(int dummy) +{ + struct cli_state *cli; + BOOL ret = True; + TALLOC_CTX *mem_ctx; + int fnum = -1; + const char *fnum_fname = "\\torture_sfileinfo.txt"; + const char *fnum_fname_new = "\\torture_sfileinfo-new.txt"; + const char *path_fname = "\\torture_spathinfo13.txt"; + const char *path_fname_new = "\\torture_spathinfo-new.txt"; + union smb_fileinfo finfo1, finfo2; + union smb_setfileinfo sfinfo; + NTSTATUS status, status2; + const char *call_name; + time_t basetime = (time(NULL) - 86400) & ~1; + BOOL check_fnum; + + if (!torture_open_connection(&cli)) { + return False; + } + + mem_ctx = talloc_init("torture_sfileinfo"); + +#define RECREATE_FILE(fname) do { \ + if (fnum != -1) cli_close(cli, fnum); \ + fnum = create_complex_file(cli, mem_ctx, fname); \ + if (fnum == -1) { \ + printf("(%d) ERROR: open of %s failed (%s)\n", \ + __LINE__, fname, cli_errstr(cli)); \ + ret = False; \ + goto done; \ + }} while (0) + +#define RECREATE_BOTH do { \ + RECREATE_FILE(path_fname); \ + cli_close(cli, fnum); \ + RECREATE_FILE(fnum_fname); \ + } while (0) + + RECREATE_BOTH; + +#define CHECK_CALL_FNUM(call, rightstatus) do { \ + check_fnum = True; \ + call_name = #call; \ + sfinfo.generic.level = RAW_SFILEINFO_ ## call; \ + sfinfo.generic.file.fnum = fnum; \ + status = smb_raw_setfileinfo(cli->tree, &sfinfo); \ + if (!NT_STATUS_EQUAL(status, rightstatus)) { \ + printf("(%d) %s - %s (should be %s)\n", __LINE__, #call, \ + nt_errstr(status), nt_errstr(rightstatus)); \ + ret = False; \ + } \ + finfo1.generic.level = RAW_FILEINFO_ALL_INFO; \ + finfo1.generic.in.fnum = fnum; \ + status2 = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo1); \ + if (!NT_STATUS_IS_OK(status2)) { \ + printf("(%d) %s pathinfo - %s\n", __LINE__, #call, nt_errstr(status)); \ + ret = False; \ + }} while (0) + +#define CHECK_CALL_PATH(call, rightstatus) do { \ + check_fnum = False; \ + call_name = #call; \ + sfinfo.generic.level = RAW_SFILEINFO_ ## call; \ + sfinfo.generic.file.fname = path_fname; \ + status = smb_raw_setpathinfo(cli->tree, &sfinfo); \ + if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { \ + sfinfo.generic.file.fname = path_fname_new; \ + status = smb_raw_setpathinfo(cli->tree, &sfinfo); \ + } \ + if (!NT_STATUS_EQUAL(status, rightstatus)) { \ + printf("(%d) %s - %s (should be %s)\n", __LINE__, #call, \ + nt_errstr(status), nt_errstr(rightstatus)); \ + ret = False; \ + } \ + finfo1.generic.level = RAW_FILEINFO_ALL_INFO; \ + finfo1.generic.in.fname = path_fname; \ + status2 = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo1); \ + if (NT_STATUS_EQUAL(status2, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { \ + finfo1.generic.in.fname = path_fname_new; \ + status2 = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo1); \ + } \ + if (!NT_STATUS_IS_OK(status2)) { \ + printf("(%d) %s pathinfo - %s\n", __LINE__, #call, nt_errstr(status2)); \ + ret = False; \ + }} while (0) + +#define CHECK1(call) \ + do { if (NT_STATUS_IS_OK(status)) { \ + finfo2.generic.level = RAW_FILEINFO_ ## call; \ + if (check_fnum) { \ + finfo2.generic.in.fnum = fnum; \ + status2 = smb_raw_fileinfo(cli->tree, mem_ctx, &finfo2); \ + } else { \ + finfo2.generic.in.fname = path_fname; \ + status2 = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo2); \ + if (NT_STATUS_EQUAL(status2, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { \ + finfo2.generic.in.fname = path_fname_new; \ + status2 = smb_raw_pathinfo(cli->tree, mem_ctx, &finfo2); \ + } \ + } \ + if (!NT_STATUS_IS_OK(status2)) { \ + printf("%s - %s\n", #call, nt_errstr(status2)); \ + } \ + }} while (0) + +#define CHECK_VALUE(call, stype, field, value) do { \ + CHECK1(call); \ + if (NT_STATUS_IS_OK(status) && finfo2.stype.out.field != value) { \ + printf("(%d) %s - %s/%s should be 0x%x - 0x%x\n", __LINE__, \ + call_name, #stype, #field, \ + (uint_t)value, (uint_t)finfo2.stype.out.field); \ + dump_all_info(mem_ctx, &finfo1); \ + }} while (0) + +#define CHECK_TIME(call, stype, field, value) do { \ + CHECK1(call); \ + if (NT_STATUS_IS_OK(status) && nt_time_to_unix(&finfo2.stype.out.field) != value) { \ + printf("(%d) %s - %s/%s should be 0x%x - 0x%x\n", __LINE__, \ + call_name, #stype, #field, \ + (uint_t)value, \ + (uint_t)nt_time_to_unix(&finfo2.stype.out.field)); \ + printf("\t%s", http_timestring(value)); \ + printf("\t%s\n", nt_time_string(mem_ctx, &finfo2.stype.out.field)); \ + dump_all_info(mem_ctx, &finfo1); \ + }} while (0) + +#define CHECK_STR(call, stype, field, value) do { \ + CHECK1(call); \ + if (NT_STATUS_IS_OK(status) && strcmp(finfo2.stype.out.field, value) != 0) { \ + printf("(%d) %s - %s/%s should be '%s' - '%s'\n", __LINE__, \ + call_name, #stype, #field, \ + value, \ + finfo2.stype.out.field); \ + dump_all_info(mem_ctx, &finfo1); \ + }} while (0) + + /* test setattr */ + sfinfo.setattr.in.attrib = FILE_ATTRIBUTE_READONLY; + sfinfo.setattr.in.write_time = basetime; + CHECK_CALL_PATH(SETATTR, NT_STATUS_OK); + CHECK_VALUE (ALL_INFO, all_info, attrib, FILE_ATTRIBUTE_READONLY); + CHECK_TIME (ALL_INFO, all_info, write_time, basetime); + + /* a zero write_time means don't change */ + sfinfo.setattr.in.attrib = 0; + sfinfo.setattr.in.write_time = 0; + CHECK_CALL_PATH(SETATTR, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, attrib, FILE_ATTRIBUTE_NORMAL); + CHECK_TIME (ALL_INFO, all_info, write_time, basetime); + + /* test setattre */ + sfinfo.setattre.in.create_time = basetime + 20; + sfinfo.setattre.in.access_time = basetime + 30; + sfinfo.setattre.in.write_time = basetime + 40; + CHECK_CALL_FNUM(SETATTRE, NT_STATUS_OK); + CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 20); + CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 30); + CHECK_TIME(ALL_INFO, all_info, write_time, basetime + 40); + + sfinfo.setattre.in.create_time = 0; + sfinfo.setattre.in.access_time = 0; + sfinfo.setattre.in.write_time = 0; + CHECK_CALL_FNUM(SETATTRE, NT_STATUS_OK); + CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 20); + CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 30); + CHECK_TIME(ALL_INFO, all_info, write_time, basetime + 40); + + /* test standard level */ + sfinfo.standard.in.create_time = basetime + 100; + sfinfo.standard.in.access_time = basetime + 200; + sfinfo.standard.in.write_time = basetime + 300; + CHECK_CALL_FNUM(STANDARD, NT_STATUS_OK); + CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 100); + CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 200); + CHECK_TIME(ALL_INFO, all_info, write_time, basetime + 300); + + /* test basic_info level */ + basetime += 86400; + unix_to_nt_time(&sfinfo.basic_info.in.create_time, basetime + 100); + unix_to_nt_time(&sfinfo.basic_info.in.access_time, basetime + 200); + unix_to_nt_time(&sfinfo.basic_info.in.write_time, basetime + 300); + unix_to_nt_time(&sfinfo.basic_info.in.change_time, basetime + 400); + sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_READONLY; + CHECK_CALL_FNUM(BASIC_INFO, NT_STATUS_OK); + CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 100); + CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 200); + CHECK_TIME(ALL_INFO, all_info, write_time, basetime + 300); + CHECK_TIME(ALL_INFO, all_info, change_time, basetime + 400); + CHECK_VALUE(ALL_INFO, all_info, attrib, FILE_ATTRIBUTE_READONLY); + + /* a zero time means don't change */ + unix_to_nt_time(&sfinfo.basic_info.in.create_time, 0); + unix_to_nt_time(&sfinfo.basic_info.in.access_time, 0); + unix_to_nt_time(&sfinfo.basic_info.in.write_time, 0); + unix_to_nt_time(&sfinfo.basic_info.in.change_time, 0); + sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_NORMAL; + CHECK_CALL_FNUM(BASIC_INFO, NT_STATUS_OK); + CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 100); + CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 200); + CHECK_TIME(ALL_INFO, all_info, write_time, basetime + 300); + CHECK_TIME(ALL_INFO, all_info, change_time, basetime + 400); + CHECK_VALUE(ALL_INFO, all_info, attrib, FILE_ATTRIBUTE_NORMAL); + + /* test basic_information level */ + basetime += 86400; + unix_to_nt_time(&sfinfo.basic_info.in.create_time, basetime + 100); + unix_to_nt_time(&sfinfo.basic_info.in.access_time, basetime + 200); + unix_to_nt_time(&sfinfo.basic_info.in.write_time, basetime + 300); + unix_to_nt_time(&sfinfo.basic_info.in.change_time, basetime + 400); + sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_READONLY; + CHECK_CALL_FNUM(BASIC_INFORMATION, NT_STATUS_OK); + CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 100); + CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 200); + CHECK_TIME(ALL_INFO, all_info, write_time, basetime + 300); + CHECK_TIME(ALL_INFO, all_info, change_time, basetime + 400); + CHECK_VALUE(ALL_INFO, all_info, attrib, FILE_ATTRIBUTE_READONLY); + + CHECK_CALL_PATH(BASIC_INFORMATION, NT_STATUS_OK); + CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 100); + CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 200); + CHECK_TIME(ALL_INFO, all_info, write_time, basetime + 300); + CHECK_TIME(ALL_INFO, all_info, change_time, basetime + 400); + CHECK_VALUE(ALL_INFO, all_info, attrib, FILE_ATTRIBUTE_READONLY); + + /* a zero time means don't change */ + unix_to_nt_time(&sfinfo.basic_info.in.create_time, 0); + unix_to_nt_time(&sfinfo.basic_info.in.access_time, 0); + unix_to_nt_time(&sfinfo.basic_info.in.write_time, 0); + unix_to_nt_time(&sfinfo.basic_info.in.change_time, 0); + sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_NORMAL; + CHECK_CALL_FNUM(BASIC_INFORMATION, NT_STATUS_OK); + CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 100); + CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 200); + CHECK_TIME(ALL_INFO, all_info, write_time, basetime + 300); + CHECK_TIME(ALL_INFO, all_info, change_time, basetime + 400); + CHECK_VALUE(ALL_INFO, all_info, attrib, FILE_ATTRIBUTE_NORMAL); + + CHECK_CALL_PATH(BASIC_INFORMATION, NT_STATUS_OK); + CHECK_TIME(ALL_INFO, all_info, create_time, basetime + 100); + CHECK_TIME(ALL_INFO, all_info, access_time, basetime + 200); + CHECK_TIME(ALL_INFO, all_info, write_time, basetime + 300); + /* interesting - w2k3 leaves change_time as current time for 0 change time + in setpathinfo + CHECK_TIME(ALL_INFO, all_info, change_time, basetime + 400); + */ + CHECK_VALUE(ALL_INFO, all_info, attrib, FILE_ATTRIBUTE_NORMAL); + + /* test disposition_info level */ + sfinfo.disposition_info.in.delete_on_close = 1; + CHECK_CALL_FNUM(DISPOSITION_INFO, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, delete_pending, 1); + CHECK_VALUE(ALL_INFO, all_info, nlink, 0); + + sfinfo.disposition_info.in.delete_on_close = 0; + CHECK_CALL_FNUM(DISPOSITION_INFO, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, delete_pending, 0); + CHECK_VALUE(ALL_INFO, all_info, nlink, 1); + + /* test disposition_information level */ + sfinfo.disposition_info.in.delete_on_close = 1; + CHECK_CALL_FNUM(DISPOSITION_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, delete_pending, 1); + CHECK_VALUE(ALL_INFO, all_info, nlink, 0); + + /* this would delete the file! */ + /* + CHECK_CALL_PATH(DISPOSITION_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, delete_pending, 1); + CHECK_VALUE(ALL_INFO, all_info, nlink, 0); + */ + + sfinfo.disposition_info.in.delete_on_close = 0; + CHECK_CALL_FNUM(DISPOSITION_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, delete_pending, 0); + CHECK_VALUE(ALL_INFO, all_info, nlink, 1); + + CHECK_CALL_PATH(DISPOSITION_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, delete_pending, 0); + CHECK_VALUE(ALL_INFO, all_info, nlink, 1); + + /* test allocation_info level - this can truncate the file + to the rounded up size */ + sfinfo.allocation_info.in.alloc_size = 0; + CHECK_CALL_FNUM(ALLOCATION_INFO, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, size, 0); + CHECK_VALUE(ALL_INFO, all_info, alloc_size, 0); + + sfinfo.allocation_info.in.alloc_size = 4096; + CHECK_CALL_FNUM(ALLOCATION_INFO, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, alloc_size, 4096); + CHECK_VALUE(ALL_INFO, all_info, size, 0); + + RECREATE_BOTH; + sfinfo.allocation_info.in.alloc_size = 0; + CHECK_CALL_FNUM(ALLOCATION_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, size, 0); + CHECK_VALUE(ALL_INFO, all_info, alloc_size, 0); + + CHECK_CALL_PATH(ALLOCATION_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, size, 0); + CHECK_VALUE(ALL_INFO, all_info, alloc_size, 0); + + sfinfo.allocation_info.in.alloc_size = 4096; + CHECK_CALL_FNUM(ALLOCATION_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, alloc_size, 4096); + CHECK_VALUE(ALL_INFO, all_info, size, 0); + + /* setting the allocation size up via setpathinfo seems + to be broken in w2k3 */ + CHECK_CALL_PATH(ALLOCATION_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, alloc_size, 0); + CHECK_VALUE(ALL_INFO, all_info, size, 0); + + /* test end_of_file_info level */ + sfinfo.end_of_file_info.in.size = 37; + CHECK_CALL_FNUM(END_OF_FILE_INFO, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, size, 37); + + sfinfo.end_of_file_info.in.size = 7; + CHECK_CALL_FNUM(END_OF_FILE_INFO, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, size, 7); + + sfinfo.end_of_file_info.in.size = 37; + CHECK_CALL_FNUM(END_OF_FILE_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, size, 37); + + CHECK_CALL_PATH(END_OF_FILE_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, size, 37); + + sfinfo.end_of_file_info.in.size = 7; + CHECK_CALL_FNUM(END_OF_FILE_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, size, 7); + + CHECK_CALL_PATH(END_OF_FILE_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(ALL_INFO, all_info, size, 7); + + + /* test position_information level */ + sfinfo.position_information.in.position = 123456; + CHECK_CALL_FNUM(POSITION_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(POSITION_INFORMATION, position_information, position, 123456); + + CHECK_CALL_PATH(POSITION_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(POSITION_INFORMATION, position_information, position, 0); + + /* test mode_information level */ + sfinfo.mode_information.in.mode = 2; + CHECK_CALL_FNUM(MODE_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(MODE_INFORMATION, mode_information, mode, 2); + + CHECK_CALL_PATH(MODE_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(MODE_INFORMATION, mode_information, mode, 0); + + sfinfo.mode_information.in.mode = 1; + CHECK_CALL_FNUM(MODE_INFORMATION, NT_STATUS_INVALID_PARAMETER); + CHECK_CALL_PATH(MODE_INFORMATION, NT_STATUS_INVALID_PARAMETER); + + sfinfo.mode_information.in.mode = 0; + CHECK_CALL_FNUM(MODE_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(MODE_INFORMATION, mode_information, mode, 0); + + CHECK_CALL_PATH(MODE_INFORMATION, NT_STATUS_OK); + CHECK_VALUE(MODE_INFORMATION, mode_information, mode, 0); + + /* finally the rename_information level */ + cli_close(cli, create_complex_file(cli, mem_ctx, fnum_fname_new)); + cli_close(cli, create_complex_file(cli, mem_ctx, path_fname_new)); + + sfinfo.rename_information.in.overwrite = 0; + sfinfo.rename_information.in.root_fid = 0; + sfinfo.rename_information.in.new_name = fnum_fname_new+1; + CHECK_CALL_FNUM(RENAME_INFORMATION, NT_STATUS_OBJECT_NAME_COLLISION); + + sfinfo.rename_information.in.new_name = path_fname_new+1; + CHECK_CALL_PATH(RENAME_INFORMATION, NT_STATUS_OBJECT_NAME_COLLISION); + + sfinfo.rename_information.in.new_name = fnum_fname_new+1; + sfinfo.rename_information.in.overwrite = 1; + CHECK_CALL_FNUM(RENAME_INFORMATION, NT_STATUS_OK); + CHECK_STR(NAME_INFO, name_info, fname.s, fnum_fname_new); + + sfinfo.rename_information.in.new_name = path_fname_new+1; + CHECK_CALL_PATH(RENAME_INFORMATION, NT_STATUS_OK); + CHECK_STR(NAME_INFO, name_info, fname.s, path_fname_new); + + sfinfo.rename_information.in.new_name = fnum_fname+1; + CHECK_CALL_FNUM(RENAME_INFORMATION, NT_STATUS_OK); + CHECK_STR(NAME_INFO, name_info, fname.s, fnum_fname); + + sfinfo.rename_information.in.new_name = path_fname+1; + CHECK_CALL_PATH(RENAME_INFORMATION, NT_STATUS_OK); + CHECK_STR(NAME_INFO, name_info, fname.s, path_fname); + + +done: + cli_close(cli, fnum); + if (!cli_unlink(cli, fnum_fname)) { + printf("Failed to delete %s - %s\n", fnum_fname, cli_errstr(cli)); + } + if (!cli_unlink(cli, path_fname)) { + printf("Failed to delete %s - %s\n", path_fname, cli_errstr(cli)); + } + + torture_close_connection(cli); + talloc_destroy(mem_ctx); + return ret; +} + + +/* + look for the w2k3 setpathinfo STANDARD bug +*/ +BOOL torture_sfileinfo_bug(int dummy) +{ + struct cli_state *cli; + TALLOC_CTX *mem_ctx; + const char *fname = "\\bug3.txt"; + union smb_setfileinfo sfinfo; + NTSTATUS status; + int fnum; + + if (!torture_open_connection(&cli)) { + return False; + } + + mem_ctx = talloc_init("torture_sfileinfo"); + + fnum = create_complex_file(cli, mem_ctx, fname); + cli_close(cli, fnum); + + sfinfo.generic.level = RAW_SFILEINFO_STANDARD; + sfinfo.generic.file.fname = fname; + + sfinfo.standard.in.create_time = 0; + sfinfo.standard.in.access_time = 0; + sfinfo.standard.in.write_time = 0; + + status = smb_raw_setpathinfo(cli->tree, &sfinfo); + printf("%s - %s\n", fname, nt_errstr(status)); + + printf("now try and delete %s\n", fname); + + return True; +} diff --git a/source4/torture/t_strcmp.c b/source4/torture/t_strcmp.c new file mode 100644 index 0000000000..622769001b --- /dev/null +++ b/source4/torture/t_strcmp.c @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2003 by Martin Pool + * + * Test harness for StrCaseCmp + */ + +#include "includes.h" + +int main(int argc, char *argv[]) +{ + if (argc != 3) { + fprintf(stderr, "usage: %s STRING1 STRING2\nCompares two strings\n", + argv[0]); + return 2; + } + + printf("%d\n", StrCaseCmp(argv[1], argv[2])); + + return 0; +} diff --git a/source4/torture/torture.c b/source4/torture/torture.c new file mode 100644 index 0000000000..05a2979dd1 --- /dev/null +++ b/source4/torture/torture.c @@ -0,0 +1,4183 @@ +/* + Unix SMB/CIFS implementation. + SMB torture tester + Copyright (C) Andrew Tridgell 1997-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 nprocs=4; +int torture_numops=100; +int torture_entries=1000; +int torture_failures=1; +static int procnum; /* records process count number when forking */ +static struct cli_state *current_cli; +static char *randomfname; +static BOOL use_oplocks; +static BOOL use_level_II_oplocks; +static const char *client_txt = "client_oplocks.txt"; +static BOOL use_kerberos; +static BOOL bypass_io; + +BOOL torture_showall = False; + +static double create_procs(BOOL (*fn)(int), BOOL *result); + +#define CHECK_MAX_FAILURES(label) do { if (++failures >= torture_failures) goto label; } while (0) + +static struct cli_state *open_nbt_connection(void) +{ + struct nmb_name called, calling; + struct in_addr ip; + struct cli_state *cli; + char *host = lp_parm_string(-1, "torture", "host"); + + make_nmb_name(&calling, lp_netbios_name(), 0x0); + make_nmb_name(&called , host, 0x20); + + zero_ip(&ip); + + cli = cli_state_init(); + if (!cli) { + printf("Failed initialize cli_struct to connect with %s\n", host); + return NULL; + } + + if (!cli_socket_connect(cli, host, &ip)) { + printf("Failed to connect with %s\n", host); + return cli; + } + + cli->transport->socket->timeout = 120000; /* set a really long timeout (2 minutes) */ + + if (!cli_transport_establish(cli, &calling, &called)) { + /* + * Well, that failed, try *SMBSERVER ... + * However, we must reconnect as well ... + */ + if (!cli_socket_connect(cli, host, &ip)) { + printf("Failed to connect with %s\n", host); + return False; + } + + make_nmb_name(&called, "*SMBSERVER", 0x20); + if (!cli_transport_establish(cli, &calling, &called)) { + printf("%s rejected the session\n",host); + printf("We tried with a called name of %s & %s\n", + host, "*SMBSERVER"); + cli_shutdown(cli); + return NULL; + } + } + + return cli; +} + +BOOL torture_open_connection(struct cli_state **c) +{ + BOOL retry; + int flags = 0; + NTSTATUS status; + char *host = lp_parm_string(-1, "torture", "host"); + char *share = lp_parm_string(-1, "torture", "share"); + char *username = lp_parm_string(-1, "torture", "username"); + char *password = lp_parm_string(-1, "torture", "password"); + + if (use_kerberos) + flags |= CLI_FULL_CONNECTION_USE_KERBEROS; + + status = cli_full_connection(c, lp_netbios_name(), + host, NULL, + share, "?????", + username, lp_workgroup(), + password, flags, &retry); + if (!NT_STATUS_IS_OK(status)) { + return False; + } + + (*c)->transport->options.use_oplocks = use_oplocks; + (*c)->transport->options.use_level2_oplocks = use_level_II_oplocks; + (*c)->transport->socket->timeout = 120000; + + return True; +} + +BOOL torture_close_connection(struct cli_state *c) +{ + BOOL ret = True; + DEBUG(9,("torture_close_connection: cli_state@%p\n", c)); + if (!c) return True; + if (!cli_tdis(c)) { + printf("tdis failed (%s)\n", cli_errstr(c)); + ret = False; + } + DEBUG(9,("torture_close_connection: call cli_shutdown\n")); + cli_shutdown(c); + DEBUG(9,("torture_close_connection: exit\n")); + 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"; + char *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= 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:%d (%s)\n", + count, 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 %d recvd %d\n", + count, sizeof(buf)-count, 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, bytes_written; + + if (cli_deltree(c1, lockfname) == -1) { + printf("unlink failed (%s)\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; + } + + printf("Checking data integrity over %d ops\n", torture_numops); + + for (i=0;itree->tid; + vuid1 = cli->session->vuid; + + memset(&buf, 0, 4); /* init buf so valgrind won't complain */ + if (cli_write(cli, fnum1, 0, buf, 130, 4) != 4) { + printf("initial write failed (%s)\n", cli_errstr(cli)); + return False; + } + + tree1 = cli->tree; /* save old tree connection */ + if (!cli_send_tconX(cli, share, "?????", + password)) { + printf("%s refused 2nd tree connect (%s)\n", host, + cli_errstr(cli)); + cli_shutdown(cli); + return False; + } + + cnum2 = cli->tree->tid; + cnum3 = MAX(cnum1, cnum2) + 1; /* any invalid number */ + vuid2 = cli->session->vuid + 1; + + /* try a write with the wrong tid */ + cli->tree->tid = cnum2; + + if (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->tree->tid = 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->session->vuid = vuid2; + cli->tree->tid = 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->session->vuid = vuid1; + cli->tree->tid = cnum1; + + if (!cli_close(cli, fnum1)) { + printf("close failed (%s)\n", cli_errstr(cli)); + return False; + } + + cli->tree->tid = cnum2; + + if (!cli_tdis(cli)) { + printf("secondary tdis failed (%s)\n", cli_errstr(cli)); + return False; + } + + cli->tree = tree1; /* restore initial tree */ + cli->tree->tid = cnum1; + + if (!torture_close_connection(cli)) { + return False; + } + + return ret; +} + + + +static BOOL tcon_devtest(struct cli_state *cli, + const char *myshare, const char *devtype, + NTSTATUS expected_error) +{ + BOOL status; + BOOL ret; + char *password = lp_parm_string(-1, "torture", "password"); + + status = cli_send_tconX(cli, myshare, devtype, + password); + + printf("Trying share %s with devtype %s\n", myshare, devtype); + + if (NT_STATUS_IS_OK(expected_error)) { + if (status) { + ret = True; + } 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) +{ + struct cli_state *cli1 = NULL; + BOOL retry; + int flags = 0; + NTSTATUS status; + BOOL ret = True; + char *host = lp_parm_string(-1, "torture", "host"); + char *share = lp_parm_string(-1, "torture", "share"); + char *username = lp_parm_string(-1, "torture", "username"); + char *password = lp_parm_string(-1, "torture", "password"); + + status = cli_full_connection(&cli1, lp_netbios_name(), + host, NULL, + share, "?????", + username, lp_workgroup(), + password, flags, &retry); + + if (!NT_STATUS_IS_OK(status)) { + printf("could not open connection\n"); + return False; + } + + if (!tcon_devtest(cli1, "IPC$", "A:", NT_STATUS_BAD_DEVICE_TYPE)) + ret = False; + + if (!tcon_devtest(cli1, "IPC$", "?????", NT_STATUS_OK)) + ret = False; + + if (!tcon_devtest(cli1, "IPC$", "LPT:", NT_STATUS_BAD_DEVICE_TYPE)) + ret = False; + + if (!tcon_devtest(cli1, "IPC$", "IPC", NT_STATUS_OK)) + ret = False; + + if (!tcon_devtest(cli1, "IPC$", "FOOBA", NT_STATUS_BAD_DEVICE_TYPE)) + ret = False; + + if (!tcon_devtest(cli1, share, "A:", NT_STATUS_OK)) + ret = False; + + if (!tcon_devtest(cli1, share, "?????", NT_STATUS_OK)) + ret = False; + + if (!tcon_devtest(cli1, share, "LPT:", NT_STATUS_BAD_DEVICE_TYPE)) + ret = False; + + if (!tcon_devtest(cli1, share, "IPC", NT_STATUS_BAD_DEVICE_TYPE)) + ret = False; + + if (!tcon_devtest(cli1, share, "FOOBA", 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) +{ + struct cli_state *cli; + const char *fname = "\\lockt2.lck"; + int fnum1, fnum2, fnum3; + BOOL correct = True; + + if (!torture_open_connection(&cli)) { + return False; + } + + printf("starting locktest2\n"); + + cli_unlink(cli, fname); + + printf("Testing pid context\n"); + + cli->session->pid = 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->session->pid = 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->session->pid = 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->session->pid = 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->session->pid = 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) +{ + 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; + } + + printf("starting locktest3\n"); + + printf("Testing 32 bit offset ranges\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; + } + + printf("Establishing %d locks\n", torture_numops); + + for (offset=i=0;isession->pid = 1, cli_lock(cli1, fnum1, 40, 4, 0, WRITE_LOCK)) && + (cli1->session->pid = 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 = (cli1->session->pid = 1, cli_lock(cli1, fnum1, 50, 4, 0, READ_LOCK)) && + (cli1->session->pid = 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 = (cli1->session->pid = 1, cli_lock(cli1, fnum1, 100, 4, 0, WRITE_LOCK)) && + (cli1->session->pid = 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) +{ + 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; + } + + 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) +{ + struct cli_state *cli; + const char *fname[1] = { "\\lock6.txt" }; + int i; + int fnum; + NTSTATUS status; + + if (!torture_open_connection(&cli)) { + return False; + } + + 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; + } + + 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; + } + + cli1->session->pid = 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; + } + + cli1->session->pid = 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; + } + + cli1->session->pid = 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"); + } + + cli1->session->pid = 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, oldtid; + pstring buf; + + if (!torture_open_connection(&cli1) || !torture_open_connection(&cli2)) { + return False; + } + + printf("starting fdpasstest\n"); + + cli_unlink(cli1, fname); + + printf("Opening a file on connection 1\n"); + + 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; + } + + printf("writing to file on connection 1\n"); + + if (cli_write(cli1, fnum1, 0, "hello world\n", 0, 13) != 13) { + printf("write failed (%s)\n", cli_errstr(cli1)); + return False; + } + + oldtid = cli2->tree->tid; + cli2->session->vuid = cli1->session->vuid; + cli2->tree->tid = cli1->tree->tid; + cli2->session->pid = cli1->session->pid; + + printf("reading from file on connection 2\n"); + + 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); + + cli2->tree->tid = oldtid; + + torture_close_connection(cli1); + torture_close_connection(cli2); + + printf("finished fdpasstest\n"); + return True; +} + + +/* + 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; + } + + printf("starting unlink test\n"); + + cli_unlink(cli, fname); + + cli->session->pid = 1; + + printf("Opening a file\n"); + + 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; + } + + printf("Unlinking a open file\n"); + + 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"; + char *fname; + int fnums[0x11000], i; + int retries=4; + BOOL correct = True; + + cli = current_cli; + + if (retries <= 0) { + printf("failed to connect\n"); + return False; + } + + printf("Testing maximum number of open files\n"); + + for (i=0; i<0x11000; i++) { + asprintf(&fname, 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; + } + free(fname); + printf("%6d\r", i); + } + printf("%6d\n", i); + i--; + + printf("cleaning up\n"); + for (;i>=0;i--) { + asprintf(&fname, template, i,(int)getpid()); + if (!cli_close(cli, fnums[i])) { + printf("Close of fnum %d failed - %s\n", fnums[i], cli_errstr(cli)); + } + if (!cli_unlink(cli, fname)) { + printf("unlink of %s failed (%s)\n", + fname, cli_errstr(cli)); + correct = False; + } + free(fname); + printf("%6d\r", i); + } + printf("%6d\n", 0); + + printf("maxfid test finished\n"); + if (!torture_close_connection(cli)) { + correct = False; + } + return correct; +} + +/* send smb negprot commands, not reading the response */ +static BOOL run_negprot_nowait(int dummy) +{ + int i; + struct cli_state *cli; + BOOL correct = True; + + printf("starting negprot nowait test\n"); + + cli = open_nbt_connection(); + if (!cli) { + return False; + } + + printf("Establishing protocol negotiations - connect with another client\n"); + + for (i=0;i<50000;i++) { + smb_negprot_send(cli->transport, PROTOCOL_NT1); + } + + if (!torture_close_connection(cli)) { + correct = False; + } + + printf("finished negprot nowait test\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; + } + + printf("New file time is %s", ctime(&t)); + + if (abs(t - time(NULL)) > 60*60*24*10) { + printf("ERROR: SMBgetatr bug. time is %s", + ctime(&t)); + t = time(NULL); + correct = False; + } + + t2 = t-60*60*24; /* 1 day ago */ + + printf("Setting file time to %s", ctime(&t2)); + + 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; + } + + printf("Retrieved file time as %s", ctime(&t)); + + 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"; + const char *pname; + BOOL correct = True; + + printf("starting trans2 test\n"); + + if (!torture_open_connection(&cli)) { + return False; + } + + cli_unlink(cli, fname); + + printf("Testing qfileinfo\n"); + + 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; + } + + printf("Testing NAME_INFO\n"); + + if (!cli_qfilename(cli, fnum, &pname)) { + printf("ERROR: qfilename failed (%s)\n", cli_errstr(cli)); + correct = False; + } + + if (!pname || strcmp(pname, fname)) { + printf("qfilename gave different name? [%s] [%s]\n", + fname, pname); + correct = False; + } + + cli_close(cli, fnum); + 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); + + printf("Checking for sticky create times\n"); + + 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; +} + +/* + Test delete on close semantics. + */ +static BOOL run_deletetest(int dummy) +{ + struct cli_state *cli1; + 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; + } + + /* 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_RIGHTS_FILE_ALL_ACCESS, FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_DELETE, NTCREATEX_DISP_OVERWRITE_IF, + NTCREATEX_OPTIONS_DELETE_ON_CLOSE, 0); + + if (fnum1 == -1) { + printf("[1] open of %s failed (%s)\n", fname, cli_errstr(cli1)); + correct = False; + goto fail; + } + + 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_RIGHTS_FILE_ALL_ACCESS, + FILE_ATTRIBUTE_NORMAL, NTCREATEX_SHARE_ACCESS_NONE, + NTCREATEX_DISP_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_RIGHTS_FILE_ALL_ACCESS, FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE, NTCREATEX_DISP_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_RIGHTS_FILE_READ, FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE, + NTCREATEX_DISP_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_RIGHTS_FILE_READ, FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE|NTCREATEX_SHARE_ACCESS_DELETE, NTCREATEX_DISP_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, + SA_RIGHT_FILE_READ_DATA | + SA_RIGHT_FILE_WRITE_DATA | + STD_RIGHT_DELETE_ACCESS, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE, + NTCREATEX_DISP_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_RIGHTS_FILE_READ, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_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_RIGHTS_FILE_READ, + FILE_ATTRIBUTE_NORMAL, NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE|NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_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, + SA_RIGHT_FILE_READ_DATA | SA_RIGHT_FILE_WRITE_DATA, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_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, + SA_RIGHT_FILE_READ_DATA | + SA_RIGHT_FILE_WRITE_DATA | + STD_RIGHT_DELETE_ACCESS, + FILE_ATTRIBUTE_NORMAL, 0, NTCREATEX_DISP_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; + } + + fnum1 = cli_nt_create_full(cli1, fname, 0, SA_RIGHT_FILE_READ_DATA|SA_RIGHT_FILE_WRITE_DATA|STD_RIGHT_DELETE_ACCESS, + FILE_ATTRIBUTE_NORMAL, NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE|NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_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, SA_RIGHT_FILE_READ_DATA|SA_RIGHT_FILE_WRITE_DATA|STD_RIGHT_DELETE_ACCESS, + FILE_ATTRIBUTE_NORMAL, NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE|NTCREATEX_SHARE_ACCESS_DELETE, + NTCREATEX_DISP_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,SA_RIGHT_FILE_READ_DATA|SA_RIGHT_FILE_WRITE_DATA, + FILE_ATTRIBUTE_NORMAL, NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_OVERWRITE_IF, NTCREATEX_OPTIONS_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, SA_RIGHT_FILE_READ_DATA|SA_RIGHT_FILE_WRITE_DATA|STD_RIGHT_DELETE_ACCESS, + FILE_ATTRIBUTE_NORMAL, NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_OVERWRITE_IF, NTCREATEX_OPTIONS_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. */ + + cli_close(cli1, fnum1); + cli_close(cli1, fnum2); + cli_setatr(cli1, fname, 0, 0); + cli_unlink(cli1, fname); + + if (!torture_close_connection(cli1)) { + correct = False; + } + if (!torture_close_connection(cli2)) { + correct = False; + } + return correct; +} + + +/* + print out server properties + */ +static BOOL run_properties(int dummy) +{ + struct cli_state *cli; + BOOL correct = True; + + printf("starting properties test\n"); + + ZERO_STRUCT(cli); + + if (!torture_open_connection(&cli)) { + return False; + } + + d_printf("Capabilities 0x%08x\n", cli->transport->negotiate.capabilities); + + if (!torture_close_connection(cli)) { + correct = False; + } + + return correct; +} + + + +/* FIRST_DESIRED_ACCESS 0xf019f */ +#define FIRST_DESIRED_ACCESS SA_RIGHT_FILE_READ_DATA|SA_RIGHT_FILE_WRITE_DATA|SA_RIGHT_FILE_APPEND_DATA|\ + SA_RIGHT_FILE_READ_EA| /* 0xf */ \ + SA_RIGHT_FILE_WRITE_EA|SA_RIGHT_FILE_READ_ATTRIBUTES| /* 0x90 */ \ + SA_RIGHT_FILE_WRITE_ATTRIBUTES| /* 0x100 */ \ + STD_RIGHT_DELETE_ACCESS|STD_RIGHT_READ_CONTROL_ACCESS|\ + STD_RIGHT_WRITE_DAC_ACCESS|STD_RIGHT_WRITE_OWNER_ACCESS /* 0xf0000 */ +/* SECOND_DESIRED_ACCESS 0xe0080 */ +#define SECOND_DESIRED_ACCESS SA_RIGHT_FILE_READ_ATTRIBUTES| /* 0x80 */ \ + STD_RIGHT_READ_CONTROL_ACCESS|STD_RIGHT_WRITE_DAC_ACCESS|\ + STD_RIGHT_WRITE_OWNER_ACCESS /* 0xe0000 */ + +#if 0 +#define THIRD_DESIRED_ACCESS FILE_READ_ATTRIBUTES| /* 0x80 */ \ + READ_CONTROL_ACCESS|WRITE_DAC_ACCESS|\ + SA_RIGHT_FILE_READ_DATA|\ + WRITE_OWNER_ACCESS /* */ +#endif + +/* + Test ntcreate calls made by xcopy + */ +static BOOL run_xcopy(int dummy) +{ + 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, + NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_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, + NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE|NTCREATEX_SHARE_ACCESS_DELETE, NTCREATEX_DISP_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) +{ + struct cli_state *cli1; + const char *fname = "\\test.txt"; + const char *fname1 = "\\test1.txt"; + BOOL correct = True; + int fnum1; + + 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_RIGHTS_FILE_READ, FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ, NTCREATEX_DISP_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 (this is correct) - %s\n", cli_errstr(cli1)); + } else { + printf("First rename succeeded - 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_RIGHTS_FILE_READ, FILE_ATTRIBUTE_NORMAL, +#if 0 + NTCREATEX_SHARE_ACCESS_DELETE|NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_OVERWRITE_IF, 0, 0); +#else + NTCREATEX_SHARE_ACCESS_DELETE|NTCREATEX_SHARE_ACCESS_READ, NTCREATEX_DISP_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 - this should have succeeded - %s\n", cli_errstr(cli1)); + correct = False; + } else { + printf("Second rename succeeded\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, STD_RIGHT_READ_CONTROL_ACCESS, FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_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, + NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_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 - this should have succeeded - %s\n", cli_errstr(cli1)); + correct = False; + } else { + printf("Third rename succeeded\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); + + 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; + } + + while(1) { + fnum = cli_nt_create_full(cli1, pipe_name, 0, SA_RIGHT_FILE_READ_DATA, FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE, NTCREATEX_DISP_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; + int failures = 0; + + printf("starting open test\n"); + + if (!torture_open_connection(&cli1)) { + return False; + } + + cli_setatr(cli1, fname, 0, 0); + 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_close(cli1, fnum1)) { + printf("close2 failed (%s)\n", cli_errstr(cli1)); + return False; + } + + if (!cli_setatr(cli1, fname, FILE_ATTRIBUTE_READONLY, 0)) { + printf("cli_setatr failed (%s)\n", cli_errstr(cli1)); + CHECK_MAX_FAILURES(error_test1); + 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)); + CHECK_MAX_FAILURES(error_test1); + 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"); +error_test1: + 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)); + CHECK_MAX_FAILURES(error_test3); + return False; + } + + if (fsize != 20) { + printf("(3) file size != 20\n"); + CHECK_MAX_FAILURES(error_test3); + 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)); + CHECK_MAX_FAILURES(error_test3); + 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)); + CHECK_MAX_FAILURES(error_test3); + return False; + } + + if (fsize != 0) { + printf("(3) file size != 0\n"); + CHECK_MAX_FAILURES(error_test3); + return False; + } + printf("finished open test 3\n"); +error_test3: + 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)); + CHECK_MAX_FAILURES(error_test4); + 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)); + } +error_test4: + /* Test the non-io opens... */ + + if (!torture_open_connection(&cli2)) { + return False; + } + + cli_setatr(cli2, fname, 0, 0); + cli_unlink(cli2, fname); + + printf("TEST #1 testing 2 non-io opens (no delete)\n"); + + fnum1 = cli_nt_create_full(cli1, fname, 0, SA_RIGHT_FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_OVERWRITE_IF, 0, 0); + + if (fnum1 == -1) { + printf("test 1 open 1 of %s failed (%s)\n", fname, cli_errstr(cli1)); + CHECK_MAX_FAILURES(error_test10); + return False; + } + + fnum2 = cli_nt_create_full(cli2, fname, 0, SA_RIGHT_FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_OPEN_IF, 0, 0); + if (fnum2 == -1) { + printf("test 1 open 2 of %s failed (%s)\n", fname, cli_errstr(cli2)); + CHECK_MAX_FAILURES(error_test10); + 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"); +error_test10: + cli_unlink(cli1, fname); + + printf("TEST #2 testing 2 non-io opens (first with delete)\n"); + + fnum1 = cli_nt_create_full(cli1, fname, 0, STD_RIGHT_DELETE_ACCESS|SA_RIGHT_FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_OVERWRITE_IF, 0, 0); + + if (fnum1 == -1) { + printf("test 2 open 1 of %s failed (%s)\n", fname, cli_errstr(cli1)); + CHECK_MAX_FAILURES(error_test20); + return False; + } + + fnum2 = cli_nt_create_full(cli2, fname, 0, SA_RIGHT_FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_OPEN_IF, 0, 0); + + if (fnum2 == -1) { + printf("test 2 open 2 of %s failed (%s)\n", fname, cli_errstr(cli2)); + CHECK_MAX_FAILURES(error_test20); + 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"); +error_test20: + cli_unlink(cli1, fname); + + printf("TEST #3 testing 2 non-io opens (second with delete)\n"); + + fnum1 = cli_nt_create_full(cli1, fname, 0, SA_RIGHT_FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_OVERWRITE_IF, 0, 0); + + if (fnum1 == -1) { + printf("test 3 open 1 of %s failed (%s)\n", fname, cli_errstr(cli1)); + CHECK_MAX_FAILURES(error_test30); + return False; + } + + fnum2 = cli_nt_create_full(cli2, fname, 0, STD_RIGHT_DELETE_ACCESS|SA_RIGHT_FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_OPEN_IF, 0, 0); + + if (fnum2 == -1) { + printf("test 3 open 2 of %s failed (%s)\n", fname, cli_errstr(cli2)); + CHECK_MAX_FAILURES(error_test30); + 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"); +error_test30: + cli_unlink(cli1, fname); + + printf("TEST #4 testing 2 non-io opens (both with delete)\n"); + + fnum1 = cli_nt_create_full(cli1, fname, 0, STD_RIGHT_DELETE_ACCESS|SA_RIGHT_FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_OVERWRITE_IF, 0, 0); + + if (fnum1 == -1) { + printf("test 4 open 1 of %s failed (%s)\n", fname, cli_errstr(cli1)); + CHECK_MAX_FAILURES(error_test40); + return False; + } + + fnum2 = cli_nt_create_full(cli2, fname, 0, STD_RIGHT_DELETE_ACCESS|SA_RIGHT_FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_OPEN_IF, 0, 0); + + if (fnum2 != -1) { + printf("test 4 open 2 of %s SUCCEEDED - should have failed (%s)\n", fname, cli_errstr(cli2)); + CHECK_MAX_FAILURES(error_test40); + return False; + } + + printf("test 4 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"); +error_test40: + 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, STD_RIGHT_DELETE_ACCESS|SA_RIGHT_FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_DELETE, NTCREATEX_DISP_OVERWRITE_IF, 0, 0); + + if (fnum1 == -1) { + printf("test 5 open 1 of %s failed (%s)\n", fname, cli_errstr(cli1)); + CHECK_MAX_FAILURES(error_test50); + return False; + } + + fnum2 = cli_nt_create_full(cli2, fname, 0, STD_RIGHT_DELETE_ACCESS|SA_RIGHT_FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_DELETE, NTCREATEX_DISP_OPEN_IF, 0, 0); + + if (fnum2 == -1) { + printf("test 5 open 2 of %s failed (%s)\n", fname, cli_errstr(cli2)); + CHECK_MAX_FAILURES(error_test50); + 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"); +error_test50: + printf("TEST #6 testing 1 non-io open, one io open\n"); + + cli_unlink(cli1, fname); + + fnum1 = cli_nt_create_full(cli1, fname, 0, SA_RIGHT_FILE_READ_DATA, FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_OVERWRITE_IF, 0, 0); + + if (fnum1 == -1) { + printf("test 6 open 1 of %s failed (%s)\n", fname, cli_errstr(cli1)); + CHECK_MAX_FAILURES(error_test60); + return False; + } + + fnum2 = cli_nt_create_full(cli2, fname, 0, SA_RIGHT_FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ, NTCREATEX_DISP_OPEN_IF, 0, 0); + + if (fnum2 == -1) { + printf("test 6 open 2 of %s failed (%s)\n", fname, cli_errstr(cli2)); + CHECK_MAX_FAILURES(error_test60); + 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"); +error_test60: + 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, SA_RIGHT_FILE_READ_DATA, FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_OVERWRITE_IF, 0, 0); + + if (fnum1 == -1) { + printf("test 7 open 1 of %s failed (%s)\n", fname, cli_errstr(cli1)); + CHECK_MAX_FAILURES(error_test70); + return False; + } + + fnum2 = cli_nt_create_full(cli2, fname, 0, STD_RIGHT_DELETE_ACCESS|SA_RIGHT_FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_DELETE, NTCREATEX_DISP_OPEN_IF, 0, 0); + + if (fnum2 != -1) { + printf("test 7 open 2 of %s SUCCEEDED - should have failed (%s)\n", fname, cli_errstr(cli2)); + CHECK_MAX_FAILURES(error_test70); + 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"); +error_test70: + 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) +{ + struct cli_state *cli1; + const char *fname = "\\openattr.file"; + int fnum1; + BOOL correct = True; + uint16 attr; + unsigned int i, j, k, l; + int failures = 0; + + printf("starting open attr test\n"); + + if (!torture_open_connection(&cli1)) { + return False; + } + + 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, SA_RIGHT_FILE_WRITE_DATA, open_attrs_table[i], + NTCREATEX_SHARE_ACCESS_NONE, NTCREATEX_DISP_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 < ARRAY_SIZE(open_attrs_table); j++) { + fnum1 = cli_nt_create_full(cli1, fname, 0, + SA_RIGHT_FILE_READ_DATA|SA_RIGHT_FILE_WRITE_DATA, + open_attrs_table[j], + NTCREATEX_SHARE_ACCESS_NONE, + NTCREATEX_DISP_OVERWRITE, 0, 0); + + if (fnum1 == -1) { + for (l = 0; l < ARRAY_SIZE(attr_results); l++) { + if (attr_results[l].num == k) { + 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; + CHECK_MAX_FAILURES(error_exit); + } + } + 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; + CHECK_MAX_FAILURES(error_exit); + } +#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 < ARRAY_SIZE(attr_results); l++) { + if (attr_results[l].num == k) { + if (attr != attr_results[l].result_attr || + open_attrs_table[i] != attr_results[l].init_attr || + open_attrs_table[j] != attr_results[l].trunc_attr) { + printf("[%d] getatr check failed. [0x%x] trunc [0x%x] got attr 0x%x, should be 0x%x\n", + k, open_attrs_table[i], + open_attrs_table[j], + (unsigned int)attr, + attr_results[l].result_attr); + correct = False; + CHECK_MAX_FAILURES(error_exit); + } + break; + } + } + k++; + } + } +error_exit: + 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; + struct cli_state *cli; + int fnum; + double t1; + BOOL correct = True; + + printf("starting directory test\n"); + + if (!torture_open_connection(&cli)) { + return False; + } + + printf("Creating %d random filenames\n", torture_numops); + + srandom(0); + for (i=0;iname); + + if (strcmp(finfo->name, ".") == 0 || strcmp(finfo->name, "..") == 0) + return; + + if (finfo->mode & FILE_ATTRIBUTE_DIRECTORY) { + if (!cli_rmdir(pcli, fname)) + printf("del_fn: failed to rmdir %s, error=%s\n", fname, cli_errstr(pcli) ); + } else { + if (!cli_unlink(pcli, fname)) + printf("del_fn: failed to unlink %s, error=%s\n", fname, cli_errstr(pcli) ); + } + free(fname); +} + + +/* + sees what IOCTLs are supported + */ +BOOL torture_ioctl_test(int dummy) +{ + struct cli_state *cli; + uint16 device, function; + int fnum; + const char *fname = "\\ioctl.dat"; + DATA_BLOB blob; + NTSTATUS status; + struct smb_ioctl parms; + TALLOC_CTX *mem_ctx; + + if (!torture_open_connection(&cli)) { + return False; + } + + mem_ctx = talloc_init("ioctl_test"); + + 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; + } + + parms.in.request = IOCTL_QUERY_JOB_INFO; + status = smb_raw_ioctl(cli->tree, mem_ctx, &parms); + 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++) { + parms.in.request = (device << 16) | function; + status = smb_raw_ioctl(cli->tree, mem_ctx, &parms); + + if (NT_STATUS_IS_OK(status)) { + printf("ioctl device=0x%x function=0x%x OK : %d bytes\n", + device, function, blob.length); + data_blob_free(&parms.out.blob); + } + } + } + + if (!torture_close_connection(cli)) { + return False; + } + + return True; +} + + +/* + tries variants of chkpath + */ +BOOL torture_chkpath_test(int dummy) +{ + struct cli_state *cli; + int fnum; + BOOL ret; + + if (!torture_open_connection(&cli)) { + return False; + } + + printf("starting chkpath test\n"); + + printf("Testing valid and invalid paths\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 existent 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_dirtest1(int dummy) +{ + int i; + struct cli_state *cli; + int fnum, num_seen; + BOOL correct = True; + + printf("starting directory test\n"); + + if (!torture_open_connection(&cli)) { + return False; + } + + cli_list(cli, "\\LISTDIR\\*", 0, del_fn, cli); + cli_list(cli, "\\LISTDIR\\*", FILE_ATTRIBUTE_DIRECTORY, del_fn, cli); + if (cli_deltree(cli, "\\LISTDIR") == -1) { + fprintf(stderr,"Failed to deltree %s, error=%s\n", "\\LISTDIR", cli_errstr(cli)); + return False; + } + if (!cli_mkdir(cli, "\\LISTDIR")) { + fprintf(stderr,"Failed to mkdir %s, error=%s\n", "\\LISTDIR", cli_errstr(cli)); + return False; + } + + printf("Creating %d files\n", torture_entries); + + /* Create torture_entries files and torture_entries directories. */ + for (i=0;i 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-e num files(entries)\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("\t-f max failures\n"); + printf("\t-b bypass I/O (NBENCH)\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; + BOOL correct = True; + char *host, *share, *username, *password; + + setup_logging("smbtorture", DEBUG_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(); + } + + host = strdup(&argv[1][2]); + p = strchr_m(&host[2],'/'); + if (!p) { + usage(); + } + *p = 0; + share = strdup(p+1); + + if (getenv("LOGNAME")) { + username = strdup(getenv("LOGNAME")); + } + + lp_set_cmdline("torture:host", host); + lp_set_cmdline("torture:share", share); + lp_set_cmdline("torture:username", username); + lp_set_cmdline("torture:password", ""); + + argc--; + argv++; + + srandom(time(NULL)); + + while ((opt = getopt(argc, argv, "p:hW:U:n:N:O:o:e:m:Ld:Ac:ks:f:bs:")) != EOF) { + switch (opt) { + case 'p': + lp_set_cmdline("smb ports", optarg); + break; + case 'W': + lp_set_cmdline("workgroup", optarg); + break; + case 'm': + lp_set_cmdline("protocol", optarg); + break; + case 'n': + lp_set_cmdline("netbios name", optarg); + break; + case 'd': + lp_set_cmdline("debug level", optarg); + setup_logging(NULL,True); + break; + case 'O': + lp_set_cmdline("socket options", optarg); + break; + case 's': + srandom(atoi(optarg)); + break; + case 'N': + nprocs = atoi(optarg); + break; + case 'o': + torture_numops = atoi(optarg); + break; + case 'e': + torture_entries = atoi(optarg); + break; + case 'L': + use_oplocks = True; + break; + case 'A': + torture_showall = True; + 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; + username = strdup(optarg); + p = strchr_m(username,'%'); + if (p) { + *p = 0; + password = strdup(p+1); + gotpass = 1; + } + lp_set_cmdline("torture:username", username); + lp_set_cmdline("torture:password", password); + break; + case 'f': + torture_failures = atoi(optarg); + break; + case 'b': + bypass_io = True; + 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) { + lp_set_cmdline("torture:password", p); + gotpass = 1; + } + } + + printf("host=%s share=%s user=%s myname=%s\n", + host, share, username, lp_netbios_name()); + + if (argc == optind) { + printf("You must specify a test to run, or 'ALL'\n"); + } else { + for (i=optind;itree, &setfile); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to setup EAs\n"); + } + + setfile.ea_set.in.ea.name.s = "SECONDEA"; + setfile.ea_set.in.ea.value = data_blob_talloc(mem_ctx, "ValueTwo", 8); + status = smb_raw_setfileinfo(cli->tree, &setfile); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to setup EAs\n"); + } + + /* make sure all the timestamps aren't the same */ + setfile.generic.level = RAW_SFILEINFO_SETATTRE; + setfile.generic.file.fnum = fnum; + + setfile.setattre.in.create_time = t + 60; + setfile.setattre.in.access_time = t + 120; + setfile.setattre.in.write_time = t + 180; + + status = smb_raw_setfileinfo(cli->tree, &setfile); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to setup file times - %s\n", nt_errstr(status)); + } + + /* make sure all the timestamps aren't the same */ + fileinfo.generic.level = RAW_FILEINFO_GETATTRE; + fileinfo.generic.in.fnum = fnum; + + status = smb_raw_fileinfo(cli->tree, mem_ctx, &fileinfo); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to query file times - %s\n", nt_errstr(status)); + } + + if (setfile.setattre.in.create_time != fileinfo.getattre.out.create_time) { + printf("create_time not setup correctly\n"); + } + if (setfile.setattre.in.access_time != fileinfo.getattre.out.access_time) { + printf("access_time not setup correctly\n"); + } + if (setfile.setattre.in.write_time != fileinfo.getattre.out.write_time) { + printf("write_time not setup correctly\n"); + } + + return fnum; +} + + + +/* return a pointer to a anonymous shared memory segment of size "size" + which will persist across fork() but will disappear when all processes + exit + + The memory is not zeroed + + This function uses system5 shared memory. It takes advantage of a property + that the memory is not destroyed if it is attached when the id is removed + */ +void *shm_setup(int size) +{ + int shmid; + void *ret; + + shmid = shmget(IPC_PRIVATE, size, SHM_R | SHM_W); + if (shmid == -1) { + printf("can't get shared memory\n"); + exit(1); + } + ret = (void *)shmat(shmid, 0, 0); + if (!ret || ret == (void *)-1) { + printf("can't attach to shared memory\n"); + return NULL; + } + /* the following releases the ipc, but note that this process + and all its children will still have access to the memory, its + just that the shmid is no longer valid for other shm calls. This + means we don't leave behind lots of shm segments after we exit + + See Stevens "advanced programming in unix env" for details + */ + shmctl(shmid, IPC_RMID, 0); + + return ret; +} + + +/* + check that a wire string matches the flags specified + not 100% accurate, but close enough for testing +*/ +BOOL wire_bad_flags(WIRE_STRING *str, int flags) +{ + int len; + len = strlen(str->s); + if (flags & STR_TERMINATE) len++; + if ((flags & STR_UNICODE) || !getenv("CLI_FORCE_ASCII")) { + len *= 2; + } else if (flags & STR_TERMINATE_ASCII) { + len++; + } + if (str->private_length != len) { + printf("Expected wire_length %d but got %d for '%s'\n", + len, str->private_length, str->s); + return True; + } + return False; +} + +/* + return a talloced string representing a time_t for human consumption +*/ +const char *time_string(TALLOC_CTX *mem_ctx, time_t t) +{ + return talloc_strdup(mem_ctx, http_timestring(mem_ctx, t)); +} + +/* + check if 2 NTTIMEs are equal +*/ +BOOL nt_time_equal(NTTIME *t1, NTTIME *t2) +{ + return t1->low == t2->low && t1->high == t2->high; +} + +/* + dump a all_info QFILEINFO structure +*/ +void dump_all_info(TALLOC_CTX *mem_ctx, union smb_fileinfo *finfo) +{ + d_printf("\tcreate_time: %s\n", nt_time_string(mem_ctx, &finfo->all_info.out.create_time)); + d_printf("\taccess_time: %s\n", nt_time_string(mem_ctx, &finfo->all_info.out.access_time)); + d_printf("\twrite_time: %s\n", nt_time_string(mem_ctx, &finfo->all_info.out.write_time)); + d_printf("\tchange_time: %s\n", nt_time_string(mem_ctx, &finfo->all_info.out.change_time)); + d_printf("\tattrib: 0x%x\n", finfo->all_info.out.attrib); + d_printf("\talloc_size: %llu\n", (unsigned long long)finfo->all_info.out.alloc_size); + d_printf("\tsize: %llu\n", (unsigned long long)finfo->all_info.out.size); + d_printf("\tnlink: %u\n", finfo->all_info.out.nlink); + d_printf("\tdelete_pending: %u\n", finfo->all_info.out.delete_pending); + d_printf("\tdirectory: %u\n", finfo->all_info.out.directory); + d_printf("\tea_size: %u\n", finfo->all_info.out.ea_size); + d_printf("\tfname: '%s'\n", finfo->all_info.out.fname.s); +} + +/* + dump file infor by name +*/ +void torture_all_info(struct cli_tree *tree, const char *fname) +{ + TALLOC_CTX *mem_ctx = talloc_init(fname); + union smb_fileinfo finfo; + NTSTATUS status; + + finfo.generic.level = RAW_FILEINFO_ALL_INFO; + finfo.generic.in.fname = fname; + status = smb_raw_pathinfo(tree, mem_ctx, &finfo); + if (!NT_STATUS_IS_OK(status)) { + d_printf("%s - %s\n", fname, nt_errstr(status)); + return; + } + + d_printf("%s:\n", fname); + dump_all_info(mem_ctx, &finfo); + talloc_destroy(mem_ctx); +} + + +/* + split a UNC name into server and share names +*/ +BOOL split_unc_name(const char *unc, char **server, char **share) +{ + char *p = strdup(unc); + if (!p) return False; + all_string_sub(p, "\\", "/", 0); + if (strncmp(p, "//", 2) != 0) return False; + + (*server) = p+2; + p = strchr(*server, '/'); + if (!p) return False; + + *p = 0; + (*share) = p+1; + + return True; +} + +/* + split a USER%PASS pair into username and password +*/ +BOOL split_username(const char *pair, char **user, char **pass) +{ + char *p = strdup(pair); + if (!p) return False; + + (*user) = p; + + p = strchr(*user, '%'); + if (!p) return False; + + *p = 0; + (*pass) = p+1; + + return True; +} + +/* + set a attribute on a file +*/ +BOOL torture_set_file_attribute(struct cli_tree *tree, const char *fname, uint16 attrib) +{ + union smb_setfileinfo sfinfo; + NTSTATUS status; + + sfinfo.generic.level = RAW_SFILEINFO_BASIC_INFORMATION; + sfinfo.generic.file.fname = fname; + + ZERO_STRUCT(sfinfo.basic_info.in); + sfinfo.basic_info.in.attrib = attrib; + status = smb_raw_setpathinfo(tree, &sfinfo); + return NT_STATUS_IS_OK(status); +} + + +/* + set a file descriptor as sparse +*/ +NTSTATUS torture_set_sparse(struct cli_tree *tree, int fnum) +{ + struct smb_ntioctl nt; + + nt.in.function = 0x900c4; + nt.in.fnum = fnum; + nt.in.fsctl = True; + nt.in.filter = 0; + + return smb_raw_ntioctl(tree, &nt); +} diff --git a/source4/torture/utable.c b/source4/torture/utable.c new file mode 100644 index 0000000000..38e381de9b --- /dev/null +++ b/source4/torture/utable.c @@ -0,0 +1,196 @@ +/* + 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. +*/ + +#include "includes.h" + +BOOL torture_utable(int dummy) +{ + struct cli_state *cli; + fstring fname; + char *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"); + + printf("Generating valid character table\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)); + 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)); + 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; + } + + printf("Determining upper/lower case table\n"); + + 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; + + d_printf("%04x (%c)\n", c, isprint(c)?c:'.'); + + fname = form_name(c); + fnum = cli_nt_create_full(cli, fname, 0, + GENERIC_RIGHTS_FILE_ALL_ACCESS, + FILE_ATTRIBUTE_NORMAL, + NTCREATEX_SHARE_ACCESS_NONE, + NTCREATEX_DISP_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=%d c=0x%04x\n", + 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; icmd_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, char **argv) +{ + if (argc != 2) { + printf("Usage: %s \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, 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, 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, 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 " }, + { "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) +{ + char *p = cmd, **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, 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; + 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(), lp_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[]) +{ + BOOL interactive = True; + int opt; + static char *cmdstr = ""; + static char *opt_logfile=NULL; + static int opt_debuglevel; + pstring logfile; + struct cmd_set **cmd_set; + extern BOOL AllowDebugChange; + static struct vfs_state vfs; + int i; + static const char *filename = ""; + + /* 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" }, + {"logfile", 'l', POPT_ARG_STRING, &opt_logfile, 'l', "Write output to specified logfile" }, + { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_debug }, + { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_version}, + { 0, 0, 0, 0} + }; + + + setlinebuf(stdout); + + DEBUGLEVEL = 1; + AllowDebugChange = False; + + pc = poptGetContext("vfstest", argc, (const char **) argv, + long_options, 0); + + while((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case 'l': + slprintf(logfile, sizeof(logfile) - 1, "%s.client", + opt_logfile); + lp_set_logfile(logfile); + interactive = False; + break; + + case 'd': + DEBUGLEVEL = opt_debuglevel; + break; + } + } + + + 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", interactive); + if (!interactive) + reopen_logs(); + + /* 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 */ + conn_init(); + vfs.conn = conn_new(); + 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[0]) { + process_file(&vfs, filename); + return 0; + } + + /* Do anything specified with -c */ + if (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/source4/torture/vfstest.h b/source4/torture/vfstest.h new file mode 100644 index 0000000000..5910c5ce37 --- /dev/null +++ b/source4/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, + char **argv); + const char *description; + const char *usage; +}; diff --git a/source4/ubiqx/.cvsignore b/source4/ubiqx/.cvsignore new file mode 100644 index 0000000000..07da2225c7 --- /dev/null +++ b/source4/ubiqx/.cvsignore @@ -0,0 +1,3 @@ +*.po +*.po32 + diff --git a/source4/utils/.cvsignore b/source4/utils/.cvsignore new file mode 100644 index 0000000000..6b8749f64e --- /dev/null +++ b/source4/utils/.cvsignore @@ -0,0 +1 @@ +net_proto.h \ No newline at end of file diff --git a/source4/utils/debug2html.c b/source4/utils/debug2html.c new file mode 100644 index 0000000000..f9a1f43f46 --- /dev/null +++ b/source4/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
 block.
+   *
+   * ------------------------------------------------------------------------ **
+   */
+  {
+  switch( new )
+    {
+    case dbg_null:
+    case dbg_ignore:
+      return( mode );
+    case dbg_message:
+      if( dbg_message != mode )
+        {
+        /* Switching to message mode. */
+        (void)printf( "
\n" );
+        return( dbg_message );
+        }
+      break;
+    default:
+      if( dbg_message == mode )
+        {
+        /* Switching out of message mode. */
+        (void)printf( "
\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( "," ); + break; + case dbg_level: + (void)printf( "]\n " ); + break; + case dbg_sourcefile: + (void)printf( ":" ); + break; + case dbg_lineno: + (void)printf( ")" ); + break; + } + + switch( new ) + { + case dbg_timestamp: + (void)printf( "[" ); + break; + case dbg_level: + (void)printf( " " ); + 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( "<" ); + break; + case '>': + (void)printf( ">" ); + break; + case '&': + (void)printf( "&" ); + break; + case '\"': + (void)printf( """ ); + 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( "\n" ); + (void)printf( "\n\n" ); + (void)printf( " Samba Debug Output\n\n\n\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( "\n\n" ); + return( 0 ); + } /* main */ diff --git a/source4/utils/editreg.c b/source4/utils/editreg.c new file mode 100644 index 0000000000..2cf8e2c9df --- /dev/null +++ b/source4/utils/editreg.c @@ -0,0 +1,2069 @@ +/* + 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. + +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 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int verbose = 0; + +/* + * 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 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; + struct key_sec_desc_s *security; +} REG_KEY; + +/* + * The KEY_LIST struct lists sub-keys. + */ + +typedef struct key_list_s { + int key_count; + 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; + VAL_KEY *vals[1]; +} VAL_LIST; + +#ifndef MAXSUBAUTHS +#define MAXSUBAUTHS 15 +#endif + +typedef struct dom_sid_s { + unsigned char ver, auths; + unsigned char auth[6]; + unsigned int sub_auths[MAXSUBAUTHS]; +} DOM_SID; + +typedef struct ace_struct_s { + unsigned char type, flags; + unsigned int perms; /* Perhaps a better def is in order */ + DOM_SID *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; + DOM_SID *owner, *group; + ACL *sacl, *dacl; +} SEC_DESC; + +#define SEC_DESC_NON 0 +#define SEC_DESC_RES 1 +#define SEC_DESC_OCU 2 + +typedef struct key_sec_desc_s { + struct key_sec_desc_s *prev, *next; + int ref_cnt; + int state; + SEC_DESC *sec_desc; +} KEY_SEC_DESC; + + +/* + * 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 + */ + +/* + * There should eventually be one to deal with security keys as well + */ + +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); + +typedef struct regf_struct_s REGF; + +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 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; ival_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; +} + +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; +} + +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, "\\"); + strcat(new_path, key_tree->name); + + /* + * 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; +} + +/* Make, delete keys */ + +int nt_delete_val_key(VAL_KEY *val_key) +{ + + if (val_key) { + if (val_key->data_blk) free(val_key->data_blk); + free(val_key); + }; + return 1; +} + +int nt_delete_val_list(VAL_LIST *vl) +{ + int i; + + if (vl) { + for (i=0; ival_count; i++) + nt_delete_val_key(vl->vals[i]); + free(vl); + } + return 1; +} + +int nt_delete_reg_key(REG_KEY *key); +int nt_delete_key_list(KEY_LIST *key_list) +{ + int i; + + if (key_list) { + for (i=0; ikey_count; i++) + nt_delete_reg_key(key_list->keys[i]); + free(key_list); + } + return 1; +} + +int nt_delete_sid(DOM_SID *sid) +{ + + if (sid) free(sid); + return 1; + +} + +int nt_delete_ace(ACE *ace) +{ + + if (ace) { + nt_delete_sid(ace->trustee); + free(ace); + } + return 1; + +} + +int nt_delete_acl(ACL *acl) +{ + + if (acl) { + int i; + + for (i=0; inum_aces; i++) + nt_delete_ace(acl->aces[i]); + + free(acl); + } + return 1; +} + +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; +} + +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; +} + +int nt_delete_reg_key(REG_KEY *key) +{ + + if (key) { + if (key->name) free(key->name); + if (key->class_name) free(key->class_name); + + /* + * Do not delete the owner ... + */ + + if (key->sub_keys) nt_delete_key_list(key->sub_keys); + if (key->values) nt_delete_val_list(key->values); + if (key->security) nt_delete_key_sec_desc(key->security); + free(key); + } + return 1; +} + +/* + * Create/delete key lists and add delete keys to/from a list, count the keys + */ + + +/* + * Create/delete value lists, add/delete values, count them + */ + + +/* + * Create/delete security descriptors, add/delete SIDS, count SIDS, etc. + * We reference count the security descriptors. Any new reference increments + * the ref count. If we modify an SD, we copy the old one, dec the ref count + * and make the change. We also want to be able to check for equality so + * we can reduce the number of SDs in use. + */ + +/* + * Code to parse registry specification from command line or files + * + * Format: + * [cmd:]key:type:value + * + * cmd = a|d|c|add|delete|change|as|ds|cs + * + */ + + +/* + * 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 + */ + +/* 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; + +/* + * 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 + +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; +}; + +/* + * 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 OFF(f) ((f) + REGF_HDR_BLKSIZ + 4) +#define LOCN(base, f) ((base) + OFF(f)) + +/* + * All of the structures below actually have a four-byte lenght 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 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; + 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 + +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 ace_struct { + unsigned char type; + unsigned char flags; + unsigned short length; + unsigned int perms; + DOM_SID 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_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; + +const VAL_STR reg_type_names[] = { + { 1, "REG_SZ" }, + { 2, "REG_EXPAND_SZ" }, + { 3, "REG_BIN" }, + { 4, "REG_DWORD" }, + { 7, "REG_MULTI_SZ" }, + { 0, NULL }, +}; + +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 + */ +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 + */ +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: + fprintf(stderr, "Len: %d\n", len); + return uni_to_ascii(datap, ascii, len, ascii_max); + break; + + case REG_TYPE_EXPANDSZ: + return uni_to_ascii(datap, ascii, len, ascii_max); + break; + + case REG_TYPE_BIN: + asciip = ascii; + for (i=0; (i 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; + +} + +REG_KEY *nt_get_key_tree(REGF *regf, NK_HDR *nk_hdr, int size); + +int nt_set_regf_input_file(REGF *regf, char *filename) +{ + return ((regf->regfile_name = strdup(filename)) != NULL); +} + +int nt_set_regf_output_file(REGF *regf, char *filename) +{ + return ((regf->outfile_name = strdup(filename)) != NULL); +} + +/* Create a regf structure and init it */ + +REGF *nt_create_regf(void) +{ + REGF *tmp = (REGF *)malloc(sizeof(REGF)); + if (!tmp) return tmp; + bzero(tmp, sizeof(REGF)); + 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 */ +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); + + /* Free the mmap'd area */ + + if (regf->base) munmap(regf->base, regf->sbuf.st_size); + regf->base = NULL; + close(regf->fd); /* Ignore the error :-) */ + + nt_delete_reg_key(regf->root); /* 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 + */ +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, ®f->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 + */ +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 + */ + +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 dont create one if not + * found + */ + +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 + */ + +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; + } + 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. + */ +DOM_SID *dup_sid(DOM_SID *sid) +{ + DOM_SID *tmp = (DOM_SID *)malloc(sizeof(DOM_SID)); + 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; iauths&&isub_auths[i] = sid->sub_auths[i]; + } + return tmp; +} + +/* + * Allocate space for an ACE and duplicate the registry encoded one passed in + */ +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 + */ +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); + ace = (REG_ACE *)&acl->aces; + for (i=0; iaces[i] = dup_ace(ace); + ace = (REG_ACE *)((char *)ace + SVAL(&ace->length)); + /* XXX: FIXME, should handle malloc errors */ + } + + return tmp; +} + +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); + tmp->owner = dup_sid((DOM_SID *)((char *)sec_desc + IVAL(&sec_desc->owner_off))); + if (!tmp->owner) { + free(tmp); + return NULL; + } + tmp->group = dup_sid((DOM_SID *)((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; +} + +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 + * all future references to this structure + * We chould put all this unpleasantness in a function. + */ + + if (!tmp) { + tmp = (KEY_SEC_DESC *)malloc(sizeof(KEY_SEC_DESC)); + if (!tmp) return NULL; + bzero(tmp, 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 + */ +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; + } + bzero(tmp, 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, "", 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 */ + 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: + /* XXX: FIXME, free the partially allocated struct */ + return NULL; + +} + +/* + * Process a VL Header and return a list of values + */ +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; ibase, vk_off); + tmp->vals[i] = process_vk(regf, vk_hdr, BLK_SIZE(vk_hdr)); + if (!tmp->vals[i]){ + goto error; + } + } + + tmp->val_count = count; + + return tmp; + + error: + /* XXX: FIXME, free the partially allocated structure */ + return NULL; +} + +/* + * Process an LF Header and return a list of sub-keys + */ +KEY_LIST *process_lf(REGF *regf, LF_HDR *lf_hdr, int size) +{ + 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 (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; + + for (i=0; ihr[i].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)); + if (!tmp->keys[i]) { + goto error; + } + } + + return tmp; + + error: + /* XXX: FIXME, free the partially allocated structure */ + return NULL; +} + +/* + * This routine is passed a NK_HDR pointer and retrieves the entire tree + * from there down. It return a REG_KEY *. + */ +REG_KEY *nt_get_key_tree(REGF *regf, NK_HDR *nk_hdr, int size) +{ + REG_KEY *tmp = NULL; + int name_len, clsname_len, lf_off, val_off, val_count, sk_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; + bzero(tmp, 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); + + bzero(cls_name, 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. + * XXX: FIXME + */ + + tmp->class_name = strdup(cls_name); + if (!tmp->class_name) { + goto error; + } + + if (verbose) fprintf(stdout, " Class Name: %s\n", cls_name); + + } + + /* + * If there are any values, process them here + */ + + val_count = IVAL(&nk_hdr->val_cnt); + + if (val_count) { + + val_off = IVAL(&nk_hdr->val_off); + vl = (VL_TYPE *)LOCN(regf->base, 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 (sk_off != -1) { + + tmp->security = process_sk(regf, sk_hdr, sk_off, BLK_SIZE(sk_hdr)); + + } + + lf_off = IVAL(&nk_hdr->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)); + if (!tmp->sub_keys){ + goto error; + } + + } + + return tmp; + + error: + if (tmp) nt_delete_reg_key(tmp); + return NULL; +} + +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(®f_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(®f_hdr->tim1), IVAL(®f_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 + */ + + first_key = (NK_HDR *)LOCN(regf->base, IVAL(®f_hdr->first_key)); + + /* + * Now, get the registry tree by processing that NK recursively + */ + + regf->root = nt_get_key_tree(regf, first_key, BLK_SIZE(first_key)); + + assert(regf->root != NULL); + + return 1; +} + +/* + * Routines to parse a REGEDIT4 file + * + * The file consists of: + * + * REGEDIT4 + * \[[-]key-path\]\n + * * + * + * 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 + +typedef struct command_s { + int cmd; + char *key; + void *val_spec_list; +} CMD; + +/* + * 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 + */ +int regedit4_file_type(int fd) +{ + int cur_ofs = 0; + + 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); + } + + if (cur_ofs) { + lseek(fd, 0, SEEK_SET); + } + + return FMT_UNREC; +} + +CMD *regedit4_get_cmd(int fd) +{ + return NULL; +} + +int regedit4_exec_cmd(CMD *cmd) +{ + + return 0; +} + +int editreg_1_1_file_type(int fd) +{ + + return FMT_UNREC; +} + +CMD *editreg_1_1_get_cmd(int fd) +{ + return NULL; +} + +int editreg_1_1_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_1_file_type, editreg_1_1_get_cmd, editreg_1_1_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 + */ + +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 ... + */ + +int print_key(const char *path, char *name, char *class_name, int root, + int terminal, int vals) +{ + + if (terminal) fprintf(stdout, "%s\\%s\n", path, name); + + return 1; +} + +/* + * Sec Desc print functions + */ + +void print_sid(DOM_SID *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"); +} + +int print_sec(SEC_DESC *sec_desc) +{ + + fprintf(stdout, " SECURITY\n"); + fprintf(stdout, " Owner: "); + print_sid(sec_desc->owner); + fprintf(stdout, " Group: "); + print_sid(sec_desc->group); + return 1; +} + +/* + * Value print function here ... + */ +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]; + + bzero(data_asc, 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:""), + val_to_str(val_type, reg_type_names), data_asc); + return 1; +} + +void usage(void) +{ + fprintf(stderr, "Usage: editreg [-v] [-k] [-c ] \n"); + fprintf(stderr, "Version: 0.1\n\n"); + fprintf(stderr, "\n\t-v\t sets verbose mode"); + fprintf(stderr, "\n\t-c \t specifies a command file"); + fprintf(stderr, "\n"); +} + +int main(int argc, char *argv[]) +{ + REGF *regf; + extern char *optarg; + extern int optind; + int opt; + int commands = 0; + char *cmd_file = NULL; + + if (argc < 2) { + usage(); + exit(1); + } + + /* + * Now, process the arguments + */ + + while ((opt = getopt(argc, argv, "vkc:")) != EOF) { + switch (opt) { + case 'c': + commands = 1; + cmd_file = optarg; + break; + + case 'v': + verbose++; + break; + + case 'k': + break; + + default: + usage(); + exit(1); + break; + } + } + + if ((regf = nt_create_regf()) == NULL) { + fprintf(stderr, "Could not create registry object: %s\n", strerror(errno)); + exit(2); + } + + if (!nt_set_regf_input_file(regf, argv[optind])) { + fprintf(stderr, "Could not set name of registry file: %s, %s\n", + argv[1], 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); + } + + /* + * At this point, we should have a registry in memory and should be able + * to iterate over it. + */ + + nt_key_iterator(regf, regf->root, 0, "", print_key, print_sec, print_val); + return 0; +} diff --git a/source4/utils/net.c b/source4/utils/net.c new file mode 100644 index 0000000000..5c78d4d5a7 --- /dev/null +++ b/source4/utils/net.c @@ -0,0 +1,642 @@ +/* + 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 = ""; +char *opt_container = "cn=Users"; +int opt_flags = -1; +int opt_jobid = 0; +int opt_timeout = 0; +const char *opt_target_workgroup = NULL; +static int opt_machine_pass = 0; + +BOOL opt_have_ip = False; +struct in_addr opt_dest_ip; + +extern BOOL AllowDebugChange; + +/* + 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) { + 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, NULL); + + if (NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } else { + DEBUG(1,("Cannot connect to server. Error was %s\n", + nt_errstr(nt_status))); + + /* 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"); + + 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, 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; + } +} + +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 (!lookup_dc_name(lp_netbios_name(), opt_target_workgroup, &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_dc(struct in_addr *server_ip, fstring server_name, const char *domain_name) +{ + if (get_pdc_ip(domain_name, server_ip)) { + fstring dc_name; + + if (is_zero_ip(*server_ip)) + return False; + + if (!lookup_dc_name(lp_netbios_name(), domain_name, server_ip, dc_name)) + return False; + + safe_strcpy(server_name, dc_name, FSTRING_LEN); + 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, trying RPC...\n"); + } + return net_rpc_join(argc, argv); +} + +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 = lp_netbios_name(); + } + + 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(lp_netbios_name(), &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 (!secrets_fetch_domain_sid(lp_netbios_name(), &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", lp_netbios_name(), sid_str); + + if (!secrets_fetch_domain_sid(lp_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", lp_workgroup(), sid_str); + + return 0; +} + +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, MAPPING_WITHOUT_PRIV)) + 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 initrid\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}, + {"VALIDATE", net_rap_validate}, + {"GROUPMEMBER", net_rap_groupmember}, + {"ADMIN", net_rap_admin}, + {"SERVICE", net_rap_service}, + {"PASSWORD", net_rap_password}, + {"TIME", net_time}, + {"LOOKUP", net_lookup}, + {"JOIN", net_join}, + {"CACHE", net_cache}, + {"GETLOCALSID", net_getlocalsid}, + {"SETLOCALSID", net_setlocalsid}, + {"GETDOMAINSID", net_getdomainsid}, + {"MAXRID", net_maxrid}, + + {"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; + static char *servicesf = dyn_CONFIGFILE; + static char *debuglevel = NULL; + + struct poptOption long_options[] = { + {"help", 'h', POPT_ARG_NONE, 0, 'h'}, + {"workgroup", 'w', POPT_ARG_STRING, &opt_target_workgroup}, + {"myworkgroup", 'W', POPT_ARG_STRING, &opt_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}, + {"conf", 's', POPT_ARG_STRING, &servicesf}, + {"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}, + {"jobid", 'j', POPT_ARG_INT, &opt_jobid}, + {"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}, + {"debuglevel", 'd', POPT_ARG_STRING, &debuglevel}, + {NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_version}, + { 0, 0, 0, 0} + }; + + zero_ip(&opt_dest_ip); + + 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); + } + } + + if (debuglevel) { + debug_parse_levels(debuglevel); + AllowDebugChange = False; + } + + lp_load(servicesf,True,False,False); + + argv_new = (const char **)poptGetArgs(pc); + + argc_new = argc; + for (i=0; i"\ +"\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 -Uadmin_username@realm%%admin_pass"\ +"\n\tchange a user's password using an admin account"\ +"\n\t(note: use realm in UPPERCASE)\n"\ +"\nnet ads chostpass"\ +"\n\tchange the trust account password of this machine in the AD tree\n"\ +"\nnet ads printer [info | publish | remove] "\ +"\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, NULL, 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, NULL, 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)); + + 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; + + ads = ads_init(NULL, NULL, opt_host); + + if (!opt_user_name) { + opt_user_name = "administrator"; + } + + if (opt_user_specified) { + need_password = True; + } + +retry: + if (!opt_password && need_password) { + char *prompt; + asprintf(&prompt,"%s password: ", opt_user_name); + opt_password = getpass(prompt); + free(prompt); + } + + if (opt_password) { + use_in_memory_ccache(); + ads->auth.password = strdup(opt_password); + } + + ads->auth.user_name = strdup(opt_user_name); + + 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; + 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 %-50.50s\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 = 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, lp_netbios_name()); + 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", lp_netbios_name()); + 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) { + char *user_name; + asprintf(&user_name, "%s$", lp_netbios_name()); + opt_password = secrets_fetch_machine_password(); + opt_user_name = user_name; + } + + if (!(ads = ads_startup())) { + return -1; + } + + rc = ads_leave_realm(ads, lp_netbios_name()); + if (!ADS_ERR_OK(rc)) { + d_printf("Failed to delete host '%s' from the '%s' realm.\n", + lp_netbios_name(), ads->config.realm); + return -1; + } + + d_printf("Removed '%s' from realm '%s'\n", lp_netbios_name(), ads->config.realm); + + return 0; +} + +static int net_ads_join_ok(void) +{ + char *user_name; + ADS_STRUCT *ads = NULL; + + if (!secrets_init()) { + DEBUG(1,("Failed to initialise secrets database\n")); + return -1; + } + + asprintf(&user_name, "%s$", lp_netbios_name()); + opt_user_name = user_name; + opt_password = secrets_fetch_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 *tmp_password; + const char *org_unit = "Computers"; + char *dn; + void *res; + DOM_SID dom_sid; + char *ou_str; + + 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; + + 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 == 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, lp_netbios_name(), 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; + } + + rc = ads_set_machine_password(ads, lp_netbios_name(), password); + if (!ADS_ERR_OK(rc)) { + d_printf("ads_set_machine_password: %s\n", ads_errstr(rc)); + return -1; + } + + 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)) { + DEBUG(1,("Failed to save machine password\n")); + return -1; + } + + d_printf("Joined '%s' to realm '%s'\n", lp_netbios_name(), ads->config.realm); + + free(password); + + return 0; +} + +int net_ads_printer_usage(int argc, const char **argv) +{ + d_printf( +"\nnet ads printer info " +"\n\tlookup info in directory for printer on server" +"\n\t(note: printer defaults to \"*\", server defaults to local)\n" +"\nnet ads printer publish " +"\n\tpublish printer in directory" +"\n\t(note: printer name is required)\n" +"\nnet ads printer remove " +"\n\tremove printer from directory" +"\n\t(note: printer name is required)\n"); + return -1; +} + +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 = lp_netbios_name(); + + 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; + 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); + + if (argc == 2) + servername = argv[1]; + else + servername = lp_netbios_name(); + + ads_find_machine_acct(ads, &res, servername); + 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], argv[0], srv_dn); + + resolve_name(servername, &server_ip, 0x20); + + nt_status = cli_full_connection(&cli, lp_netbios_name(), servername, + &server_ip, 0, + "IPC$", "IPC", + opt_user_name, opt_workgroup, + opt_password ? opt_password : "", + CLI_FULL_CONNECTION_USE_KERBEROS, + NULL); + + cli_nt_session_open(cli, PI_SPOOLSS); + get_remote_printer_publishing_data(cli, mem_ctx, &mods, argv[0]); + + 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 = lp_netbios_name(); + + 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[] = { + {"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; + char *prompt; + ADS_STATUS ret; + + + if ((argc != 1) || (opt_user_name == NULL) || + (opt_password == NULL) || (strchr(opt_user_name, '@') == NULL) || + (strchr(argv[0], '@') == NULL)) { + return net_ads_usage(argc, argv); + } + + use_in_memory_ccache(); + c = strchr(auth_principal, '@'); + realm = ++c; + + /* 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; + + asprintf(&prompt, "Enter new password for %s:", argv[0]); + + new_password = getpass(prompt); + + ret = kerberos_set_password(ads->auth.kdc_server, auth_principal, + auth_password, argv[0], new_password, ads->auth.time_offset); + if (!ADS_ERR_OK(ret)) { + d_printf("Password change failed :-( ...\n"); + ads_destroy(&ads); + free(prompt); + return -1; + } + + d_printf("Password change for %s completed.\n", argv[0]); + ads_destroy(&ads); + free(prompt); + + return 0; +} + + +static int net_ads_change_localhost_pass(int argc, const char **argv) +{ + ADS_STRUCT *ads; + char *host_principal; + char *hostname; + ADS_STATUS ret; + char *user_name; + + if (!secrets_init()) { + DEBUG(1,("Failed to initialise secrets database\n")); + return -1; + } + + asprintf(&user_name, "%s$", lp_netbios_name()); + opt_user_name = user_name; + + opt_password = secrets_fetch_machine_password(); + + use_in_memory_ccache(); + + if (!(ads = ads_startup())) { + return -1; + } + + hostname = strdup(lp_netbios_name()); + strlower(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 \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 *exp; + const char **attrs; + void *res = NULL; + + if (argc < 1) { + return net_ads_search_usage(argc, argv); + } + + if (!(ads = ads_startup())) { + return -1; + } + + exp = argv[0]; + attrs = (argv + 1); + + rc = ads_do_search_all(ads, ads->config.bind_path, + LDAP_SCOPE_SUBTREE, + 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 \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}, + {"CHOSTPASS", net_ads_change_localhost_pass}, +#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}, + {"CHOSTPASS", net_ads_change_localhost_pass}, + {"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_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/source4/utils/net_ads_cldap.c b/source4/utils/net_ads_cldap.c new file mode 100644 index 0000000000..132238533d --- /dev/null +++ b/source4/utils/net_ads_cldap.c @@ -0,0 +1,354 @@ +/* + 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 + +struct netlogon_string { + uint32 comp_len; + char **component; + uint8 extra_flag; +}; + +struct cldap_netlogon_reply { + uint32 type; + uint32 flags; + GUID guid; + + struct netlogon_string forest; + struct netlogon_string domain; + struct netlogon_string hostname; + + struct netlogon_string netbios_domain; + struct netlogon_string netbios_hostname; + + struct netlogon_string user_name; + struct netlogon_string site_name; + + struct netlogon_string unk0; + + uint32 version; + uint16 lmnt_token; + uint16 lm20_token; +}; + +/* + These strings are rather interesting... They are composed of a series of + length encoded strings, terminated by either 1) a zero length string or 2) + a 0xc0 byte with what appears to be a one byte flags immediately following. +*/ +static unsigned pull_netlogon_string(struct netlogon_string *ret,const char *d) +{ + char *p = (char *)d; + + ZERO_STRUCTP(ret); + + do { + unsigned len = (unsigned char)*p; + p++; + + if (len > 0 && len != 0xc0) { + ret->component = realloc(ret->component, + ++ret->comp_len * + sizeof(char *)); + + ret->component[ret->comp_len - 1] = + smb_xstrndup(p, len); + p += len; + } else { + if (len == 0xc0) { + ret->extra_flag = *p; + p++; + }; + break; + } + } while (1); + + return (p - d); +} + +/* + 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]; + + 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); + + 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) != data.length) { + d_printf("failed to send cldap query (%s)\n", strerror(errno)); + } + + file_save("cldap_query.dat", data.data, data.length); + 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; + + file_save("cldap_reply.dat", blob.data, blob.length); + + 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; + } + + file_save("cldap_reply_core.dat", os3.data, os3.length); + + p = os3.data; + + reply->type = IVAL(p, 0); p += 4; + reply->flags = IVAL(p, 0); p += 4; + + memcpy(&reply->guid.info, p, GUID_SIZE); + p += GUID_SIZE; + + p += pull_netlogon_string(&reply->forest, p); + p += pull_netlogon_string(&reply->domain, p); + p += pull_netlogon_string(&reply->hostname, p); + p += pull_netlogon_string(&reply->netbios_domain, p); + p += pull_netlogon_string(&reply->netbios_hostname, p); + p += pull_netlogon_string(&reply->user_name, p); + p += pull_netlogon_string(&reply->site_name, p); + + p += pull_netlogon_string(&reply->unk0, p); + + 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; +} + +/* + free a netlogon string +*/ +static void netlogon_string_free(struct netlogon_string *str) +{ + int i; + + for (i = 0; i < str->comp_len; ++i) { + SAFE_FREE(str->component[i]); + } + SAFE_FREE(str->component); +} + +/* + free a cldap reply packet +*/ +static void cldap_reply_free(struct cldap_netlogon_reply *reply) +{ + netlogon_string_free(&reply->forest); + netlogon_string_free(&reply->domain); + netlogon_string_free(&reply->hostname); + netlogon_string_free(&reply->netbios_domain); + netlogon_string_free(&reply->netbios_hostname); + netlogon_string_free(&reply->user_name); + netlogon_string_free(&reply->site_name); + netlogon_string_free(&reply->unk0); +} + +static void d_print_netlogon_string(const char *label, + struct netlogon_string *str) +{ + int i; + + if (str->comp_len) { + d_printf("%s", label); + if (str->extra_flag) { + d_printf("[%d]", str->extra_flag); + } + d_printf(": "); + for (i = 0; i < str->comp_len; ++i) { + d_printf("%s%s", (i ? "." : ""), str->component[i]); + } + d_printf("\n"); + } +} + +/* + 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, lp_netbios_name(), 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: 0x%x\n", reply.type); + d_printf("GUID: "); + print_guid(&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"); + + d_print_netlogon_string("Forest", &reply.forest); + d_print_netlogon_string("Domain", &reply.domain); + d_print_netlogon_string("Hostname", &reply.hostname); + + d_print_netlogon_string("Pre-Win2k Domain", &reply.netbios_domain); + d_print_netlogon_string("Pre-Win2k Hostname", &reply.netbios_hostname); + + d_print_netlogon_string("User name", &reply.user_name); + d_print_netlogon_string("Site Name", &reply.site_name); + d_print_netlogon_string("Unknown Field", &reply.unk0); + + 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); + + cldap_reply_free(&reply); + + return ret; +} + + +#endif diff --git a/source4/utils/net_cache.c b/source4/utils/net_cache.c new file mode 100644 index 0000000000..93c4f1aa1d --- /dev/null +++ b/source4/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", 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 \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 \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 add \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 \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 \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/source4/utils/net_help.c b/source4/utils/net_help.c new file mode 100644 index 0000000000..4000a248ff --- /dev/null +++ b/source4/utils/net_help.c @@ -0,0 +1,199 @@ +/* + 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=\t\tserver name\n"); + d_printf("\t-I or --ipaddress=\taddress of target server\n"); + d_printf("\t-w or --workgroup=\t\ttarget workgroup or domain\n"); + + d_printf("\n"); + d_printf("Valid miscellaneous options are:\n"); /* misc options */ + d_printf("\t-p or --port=\t\tconnection port on target\n"); + d_printf("\t-W or --myworkgroup=\tclient workgroup\n"); + d_printf("\t-d or --debug=\t\tdebug level (0-10)\n"); + d_printf("\t-n or --myname=\t\tclient name\n"); + d_printf("\t-U or --user=\t\tuser name\n"); + d_printf("\t-s or --conf=\t\tpathname of smb.conf file\n"); + d_printf("\t-l or --long\t\t\tDisplay full 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 \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"); + return -1; +} + +int net_help_user(int argc, const char **argv) +{ + d_printf("\nnet [] user [misc. options] [targets]"\ + "\n\tList users\n\n"); + d_printf("net [] user DELETE [misc. options] [targets]"\ + "\n\tDelete specified user\n"); + d_printf("\nnet [] user INFO [misc. options] [targets]"\ + "\n\tList the domain groups of the specified user\n"); + d_printf("\nnet [] user ADD [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=\tdescriptive comment (for add only)\n"); + d_printf("\t-c or --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 [] group [misc. options] [targets]"\ + "\n\tList user groups\n\n"); + d_printf("net [] group DELETE "\ + "[misc. options] [targets]"\ + "\n\tDelete specified group\n"); + d_printf("\nnet [] group ADD [-C comment] [-c container]"\ + " [misc. options] [targets]\n\tCreate specified group\n"); + net_common_methods_usage(argc, argv); + net_common_flags_usage(argc, argv); + d_printf("\t-C or --comment=\tdescriptive comment (for add only)\n"); + d_printf("\t-c or --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 [] 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 [] share [misc. options] [targets] \n" + "\tenumerates all exported resources (network shares) " + "on target server\n\n" + "net [] share ADD [misc. options] [targets]" + "\n\tAdds a share from a server (makes the export active)\n\n" + "net [] share DELETE [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=\tdescriptive comment (for add only)\n" + "\t-M or --maxusers=\t\tmax users allowed for share\n"); + return -1; +} + +int net_help_file(int argc, const char **argv) +{ + d_printf("net [] file [misc. options] [targets]\n"\ + "\tlists all open files on file server\n\n"); + d_printf("net [] file USER "\ + "[misc. options] [targets]"\ + "\n\tlists all files opened by username on file server\n\n"); + d_printf("net [] file CLOSE [misc. options] [targets]\n"\ + "\tcloses specified file on target server\n\n"); + d_printf("net [rap] file INFO [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; +} + +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 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"\ + "\n"\ + " net ads \tto run ADS commands\n"\ + " net rap \tto run RAP (pre-RPC) commands\n"\ + " net rpc \tto run RPC commands\n"\ + "\n"\ + "Type \"net help